From 168ab32b9f85c39b2101ab5a3e66946902c7dc5d Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Sat, 6 Dec 2025 16:06:47 -0500 Subject: [PATCH 01/68] wip: docs generation action --- .github/docs-gen-prompts.md | 121 +++ .github/scripts/check-solidity-comments.sh | 0 .../generate-docs-utils/ai-enhancement.js | 351 +++++++++ .github/scripts/generate-docs-utils/config.js | 28 + .../doc-generation-utils.js | 288 ++++++++ .../generate-docs-utils/forge-doc-parser.js | 686 ++++++++++++++++++ .../generate-docs-utils/pr-body-generator.js | 100 +++ .../templates/contract.mdx.template | 195 +++++ .../generate-docs-utils/templates/helpers.js | 115 +++ .../templates/template-engine.js | 366 ++++++++++ .../templates/templates.js | 499 +++++++++++++ .github/scripts/generate-docs.js | 374 ++++++++++ .github/scripts/workflow-utils.js | 114 ++- .github/workflows/generate-docs.yml | 159 ++++ 14 files changed, 3391 insertions(+), 5 deletions(-) create mode 100644 .github/docs-gen-prompts.md mode change 100755 => 100644 .github/scripts/check-solidity-comments.sh create mode 100644 .github/scripts/generate-docs-utils/ai-enhancement.js create mode 100644 .github/scripts/generate-docs-utils/config.js create mode 100644 .github/scripts/generate-docs-utils/doc-generation-utils.js create mode 100644 .github/scripts/generate-docs-utils/forge-doc-parser.js create mode 100644 .github/scripts/generate-docs-utils/pr-body-generator.js create mode 100644 .github/scripts/generate-docs-utils/templates/contract.mdx.template create mode 100644 .github/scripts/generate-docs-utils/templates/helpers.js create mode 100644 .github/scripts/generate-docs-utils/templates/template-engine.js create mode 100644 .github/scripts/generate-docs-utils/templates/templates.js create mode 100644 .github/scripts/generate-docs.js create mode 100644 .github/workflows/generate-docs.yml diff --git a/.github/docs-gen-prompts.md b/.github/docs-gen-prompts.md new file mode 100644 index 00000000..1a1aa7eb --- /dev/null +++ b/.github/docs-gen-prompts.md @@ -0,0 +1,121 @@ +# AI Documentation Enhancement Prompts + +This file contains all prompts and instructions used for AI-powered documentation enhancement. +Edit this file to adjust AI behavior without modifying the JavaScript code. + +--- + +## System Prompt + +You are a Solidity smart contract documentation expert for the Compose framework. +Always respond with valid JSON only, no markdown formatting. +Follow the project conventions and style guidelines strictly. + +--- + +## Relevant Guideline Sections + +These section headers from copilot-instructions.md will be extracted and appended to the system prompt. +One section per line. Must match exactly as they appear in copilot-instructions.md. + +``` +## 3. Core Philosophy +## 4. Facet Design Principles +## 5. Banned Solidity Features +## 6. Composability Guidelines +## 11. Code Style Guide +``` + +--- + +## Module Prompt Template + +Given this module documentation from the Compose diamond proxy framework, enhance it by generating: + +1. **overview**: A clear, concise overview (2-3 sentences) explaining what this module does and why it's useful in the context of diamond contracts. + +2. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this module. Show importing and calling functions. + +3. **bestPractices**: 2-3 bullet points of best practices for using this module. + +4. **integrationNotes**: A note about how this module works with diamond storage pattern and how changes made through it are visible to facets. + +5. **keyFeatures**: A brief bullet list of key features. + +Contract Information: +- Name: {{title}} +- Description: {{description}} +- Functions: {{functionNames}} +- Function Details: +{{functionDescriptions}} + +Respond ONLY with valid JSON in this exact format (no markdown code blocks, no extra text): +{ + "overview": "enhanced overview text here", + "usageExample": "solidity code here (use \\n for newlines)", + "bestPractices": "- Point 1\\n- Point 2\\n- Point 3", + "keyFeatures": "- Feature 1\\n- Feature 2", + "integrationNotes": "integration notes here" +} + +--- + +## Facet Prompt Template + +Given this facet documentation from the Compose diamond proxy framework, enhance it by generating: + +1. **overview**: A clear, concise overview (2-3 sentences) explaining what this facet does and why it's useful in the context of diamond contracts. + +2. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this facet. Show how it would be used in a diamond. + +3. **bestPractices**: 2-3 bullet points of best practices for using this facet. + +4. **securityConsiderations**: Important security considerations when using this facet (access control, reentrancy, etc.). + +5. **keyFeatures**: A brief bullet list of key features. + +Contract Information: +- Name: {{title}} +- Description: {{description}} +- Functions: {{functionNames}} +- Function Details: +{{functionDescriptions}} + +Respond ONLY with valid JSON in this exact format (no markdown code blocks, no extra text): +{ + "overview": "enhanced overview text here", + "usageExample": "solidity code here (use \\n for newlines)", + "bestPractices": "- Point 1\\n- Point 2\\n- Point 3", + "keyFeatures": "- Feature 1\\n- Feature 2", + "securityConsiderations": "security notes here" +} + +--- + +## Module Fallback Content + +Used when AI enhancement is unavailable for modules. + +### integrationNotes + +This module accesses shared diamond storage, so changes made through this module are immediately visible to facets using the same storage pattern. All functions are internal as per Compose conventions. + +### keyFeatures + +- All functions are `internal` for use in custom facets +- Follows diamond storage pattern (EIP-8042) +- Compatible with ERC-2535 diamonds +- No external dependencies or `using` directives + +--- + +## Facet Fallback Content + +Used when AI enhancement is unavailable for facets. + +### keyFeatures + +- Self-contained facet with no imports or inheritance +- Only `external` and `internal` function visibility +- Follows Compose readability-first conventions +- Ready for diamond integration diff --git a/.github/scripts/check-solidity-comments.sh b/.github/scripts/check-solidity-comments.sh old mode 100755 new mode 100644 diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js new file mode 100644 index 00000000..16b1963f --- /dev/null +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -0,0 +1,351 @@ +/** + * GitHub Models integration for documentation enhancement + * Uses Azure AI inference endpoint with GitHub token auth + * See: https://github.blog/changelog/2025-04-14-github-actions-token-integration-now-generally-available-in-github-models/ + */ + +const fs = require('fs'); +const path = require('path'); +const { models: MODELS_CONFIG } = require('./config'); +const { sleep, makeHttpsRequest } = require('../workflow-utils'); + +// Rate limiting: GitHub Models has 10 requests per 60s limit +// We add 7 seconds between calls to stay safe (60/10 = 6, +1 buffer) +const RATE_LIMIT_DELAY_MS = 8000; +let lastApiCallTime = 0; + +const AI_PROMPT_PATH = path.join(__dirname, '../../docs-gen-prompts.md'); +const REPO_INSTRUCTIONS_PATH = path.join(__dirname, '../../copilot-instructions.md'); + +/** + * Wait for rate limit if needed + * Ensures at least RATE_LIMIT_DELAY_MS between API calls + */ +async function waitForRateLimit() { + const now = Date.now(); + const elapsed = now - lastApiCallTime; + + if (lastApiCallTime > 0 && elapsed < RATE_LIMIT_DELAY_MS) { + const waitTime = RATE_LIMIT_DELAY_MS - elapsed; + console.log(` ⏳ Rate limit: waiting ${Math.ceil(waitTime / 1000)}s...`); + await sleep(waitTime); + } + + lastApiCallTime = Date.now(); +} + +// Load repository instructions for context +let REPO_INSTRUCTIONS = ''; +try { + REPO_INSTRUCTIONS = fs.readFileSync(REPO_INSTRUCTIONS_PATH, 'utf8'); +} catch (e) { + console.warn('Could not load copilot-instructions.md:', e.message); +} + +// Load AI prompts from markdown file +let AI_PROMPTS = { + systemPrompt: '', + modulePrompt: '', + facetPrompt: '', + relevantSections: [], + moduleFallback: { integrationNotes: '', keyFeatures: '' }, + facetFallback: { keyFeatures: '' }, +}; +try { + const promptsContent = fs.readFileSync(AI_PROMPT_PATH, 'utf8'); + AI_PROMPTS = parsePromptsFile(promptsContent); +} catch (e) { + console.warn('Could not load ai-prompts.md:', e.message); +} + +/** + * Parse the prompts markdown file to extract individual prompts + * @param {string} content - Raw markdown content + * @returns {object} Parsed prompts and configurations + */ +function parsePromptsFile(content) { + const sections = content.split(/^---$/m).map(s => s.trim()).filter(Boolean); + + const prompts = { + systemPrompt: '', + modulePrompt: '', + facetPrompt: '', + relevantSections: [], + moduleFallback: { integrationNotes: '', keyFeatures: '' }, + facetFallback: { keyFeatures: '' }, + }; + + for (const section of sections) { + if (section.includes('## System Prompt')) { + const match = section.match(/## System Prompt\s*\n([\s\S]*)/); + if (match) { + prompts.systemPrompt = match[1].trim(); + } + } else if (section.includes('## Relevant Guideline Sections')) { + // Extract sections from the code block + const codeMatch = section.match(/```\n([\s\S]*?)```/); + if (codeMatch) { + prompts.relevantSections = codeMatch[1] + .split('\n') + .map(s => s.trim()) + .filter(s => s.startsWith('## ')); + } + } else if (section.includes('## Module Prompt Template')) { + const match = section.match(/## Module Prompt Template\s*\n([\s\S]*)/); + if (match) { + prompts.modulePrompt = match[1].trim(); + } + } else if (section.includes('## Facet Prompt Template')) { + const match = section.match(/## Facet Prompt Template\s*\n([\s\S]*)/); + if (match) { + prompts.facetPrompt = match[1].trim(); + } + } else if (section.includes('## Module Fallback Content')) { + // Parse subsections for integrationNotes and keyFeatures + const integrationMatch = section.match(/### integrationNotes\s*\n([\s\S]*?)(?=###|$)/); + if (integrationMatch) { + prompts.moduleFallback.integrationNotes = integrationMatch[1].trim(); + } + const keyFeaturesMatch = section.match(/### keyFeatures\s*\n([\s\S]*?)(?=###|$)/); + if (keyFeaturesMatch) { + prompts.moduleFallback.keyFeatures = keyFeaturesMatch[1].trim(); + } + } else if (section.includes('## Facet Fallback Content')) { + const keyFeaturesMatch = section.match(/### keyFeatures\s*\n([\s\S]*?)(?=###|$)/); + if (keyFeaturesMatch) { + prompts.facetFallback.keyFeatures = keyFeaturesMatch[1].trim(); + } + } + } + + return prompts; +} + +/** + * Build the system prompt with repository context + * Uses the system prompt from the prompts file, or a fallback if not found + * @returns {string} System prompt for Copilot + */ +function buildSystemPrompt() { + let systemPrompt = AI_PROMPTS.systemPrompt || `You are a Solidity smart contract documentation expert for the Compose framework. +Always respond with valid JSON only, no markdown formatting. +Follow the project conventions and style guidelines strictly.`; + + if (REPO_INSTRUCTIONS) { + const relevantSections = AI_PROMPTS.relevantSections.length > 0 + ? AI_PROMPTS.relevantSections + : [ + '## 3. Core Philosophy', + '## 4. Facet Design Principles', + '## 5. Banned Solidity Features', + '## 6. Composability Guidelines', + '## 11. Code Style Guide', + ]; + + let contextSnippets = []; + for (const section of relevantSections) { + const startIdx = REPO_INSTRUCTIONS.indexOf(section); + if (startIdx !== -1) { + // Extract section content (up to next ## or 2000 chars max) + const nextSection = REPO_INSTRUCTIONS.indexOf('\n## ', startIdx + section.length); + const endIdx = nextSection !== -1 ? nextSection : startIdx + 2000; + const snippet = REPO_INSTRUCTIONS.slice(startIdx, Math.min(endIdx, startIdx + 2000)); + contextSnippets.push(snippet.trim()); + } + } + + if (contextSnippets.length > 0) { + systemPrompt += `\n\n--- PROJECT GUIDELINES ---\n${contextSnippets.join('\n\n')}`; + } + } + + return systemPrompt; +} + +/** + * Build the prompt for Copilot based on contract type + * @param {object} data - Parsed documentation data + * @param {'module' | 'facet'} contractType - Type of contract + * @returns {string} Prompt for Copilot + */ +function buildPrompt(data, contractType) { + const functionNames = data.functions.map(f => f.name).join(', '); + const functionDescriptions = data.functions + .map(f => `- ${f.name}: ${f.description || 'No description'}`) + .join('\n'); + + const promptTemplate = contractType === 'module' + ? AI_PROMPTS.modulePrompt + : AI_PROMPTS.facetPrompt; + + // If we have a template from the file, use it with variable substitution + if (promptTemplate) { + return promptTemplate + .replace(/\{\{title\}\}/g, data.title) + .replace(/\{\{description\}\}/g, data.description || 'No description provided') + .replace(/\{\{functionNames\}\}/g, functionNames || 'None') + .replace(/\{\{functionDescriptions\}\}/g, functionDescriptions || ' None'); + } + + // Fallback to hardcoded prompt if template not loaded + return `Given this ${contractType} documentation from the Compose diamond proxy framework, enhance it by generating: + +1. **overview**: A clear, concise overview (2-3 sentences) explaining what this ${contractType} does and why it's useful in the context of diamond contracts. + +2. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this ${contractType}. For modules, show importing and calling functions. For facets, show how it would be used in a diamond. + +3. **bestPractices**: 2-3 bullet points of best practices for using this ${contractType}. + +${contractType === 'module' ? '4. **integrationNotes**: A note about how this module works with diamond storage pattern and how changes made through it are visible to facets.' : ''} + +${contractType === 'facet' ? '4. **securityConsiderations**: Important security considerations when using this facet (access control, reentrancy, etc.).' : ''} + +5. **keyFeatures**: A brief bullet list of key features. + +Contract Information: +- Name: ${data.title} +- Description: ${data.description || 'No description provided'} +- Functions: ${functionNames || 'None'} +- Function Details: +${functionDescriptions || ' None'} + +Respond ONLY with valid JSON in this exact format (no markdown code blocks, no extra text): +{ + "overview": "enhanced overview text here", + "usageExample": "solidity code here (use \\n for newlines)", + "bestPractices": "- Point 1\\n- Point 2\\n- Point 3", + "keyFeatures": "- Feature 1\\n- Feature 2", + ${contractType === 'module' ? '"integrationNotes": "integration notes here"' : '"securityConsiderations": "security notes here"'} +}`; +} + +/** + * Enhance documentation data using GitHub Copilot + * @param {object} data - Parsed documentation data + * @param {'module' | 'facet'} contractType - Type of contract + * @param {string} token - GitHub token + * @returns {Promise} Enhanced data + */ +async function enhanceWithAI(data, contractType, token) { + if (!token) { + console.log(' ⚠️ No GitHub token provided, skipping AI enhancement'); + return addFallbackContent(data, contractType); + } + + const systemPrompt = buildSystemPrompt(); + const userPrompt = buildPrompt(data, contractType); + + const requestBody = JSON.stringify({ + messages: [ + { + role: 'system', + content: systemPrompt, + }, + { + role: 'user', + content: userPrompt, + }, + ], + model: MODELS_CONFIG.model, + max_tokens: MODELS_CONFIG.maxTokens, + }); + + // GitHub Models uses Azure AI inference endpoint + // Authentication: GITHUB_TOKEN works directly in GitHub Actions + const options = { + hostname: MODELS_CONFIG.host, + port: 443, + path: '/chat/completions', + method: 'POST', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'Compose-DocGen/1.0', + }, + }; + + try { + await waitForRateLimit(); + const response = await makeHttpsRequest(options, requestBody); + + if (response.choices && response.choices[0] && response.choices[0].message) { + const content = response.choices[0].message.content; + + try { + let enhanced = JSON.parse(content); + console.log('✅ AI enhancement successful'); + + return { + ...data, + overview: enhanced.overview || data.overview, + usageExample: enhanced.usageExample || null, + bestPractices: enhanced.bestPractices || null, + keyFeatures: enhanced.keyFeatures || null, + integrationNotes: enhanced.integrationNotes || null, + securityConsiderations: enhanced.securityConsiderations || null, + }; + } catch (parseError) { + console.log(' ⚠️ Could not parse API response as JSON'); + console.log(' Response:', content.substring(0, 200)); + return addFallbackContent(data, contractType); + } + } + + console.log(' ⚠️ Unexpected API response format'); + return addFallbackContent(data, contractType); + } catch (error) { + console.log(` ⚠️ GitHub Models API error: ${error.message}`); + return addFallbackContent(data, contractType); + } +} + +/** + * Add fallback content when AI is unavailable + * @param {object} data - Documentation data + * @param {'module' | 'facet'} contractType - Type of contract + * @returns {object} Data with fallback content + */ +function addFallbackContent(data, contractType) { + console.log(' Using fallback content'); + + const enhanced = { ...data }; + + if (contractType === 'module') { + enhanced.integrationNotes = AI_PROMPTS.moduleFallback.integrationNotes || + `This module accesses shared diamond storage, so changes made through this module are immediately visible to facets using the same storage pattern. All functions are internal as per Compose conventions.`; + enhanced.keyFeatures = AI_PROMPTS.moduleFallback.keyFeatures || + `- All functions are \`internal\` for use in custom facets\n- Follows diamond storage pattern (EIP-8042)\n- Compatible with ERC-2535 diamonds\n- No external dependencies or \`using\` directives`; + } else { + enhanced.keyFeatures = AI_PROMPTS.facetFallback.keyFeatures || + `- Self-contained facet with no imports or inheritance\n- Only \`external\` and \`internal\` function visibility\n- Follows Compose readability-first conventions\n- Ready for diamond integration`; + } + + return enhanced; +} + +/** + * Check if enhancement should be skipped for a file + * @param {object} data - Documentation data + * @returns {boolean} True if should skip + */ +function shouldSkipEnhancement(data) { + if (!data.functions || data.functions.length === 0) { + return true; + } + + if (data.title.startsWith('I') && data.title.length > 1 && + data.title[1] === data.title[1].toUpperCase()) { + return true; + } + + return false; +} + +module.exports = { + enhanceWithAI, + addFallbackContent, + shouldSkipEnhancement, +}; + + diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js new file mode 100644 index 00000000..6b71e32e --- /dev/null +++ b/.github/scripts/generate-docs-utils/config.js @@ -0,0 +1,28 @@ +/** + * Configuration for documentation generation + * + * Centralized configuration for paths, settings, and defaults. + * Modify this file to change documentation output paths or behavior. + */ + +module.exports = { + // Input paths + forgeDocsDir: 'docs/src/src', + + // Output paths for generated documentation + facetsOutputDir: 'website/docs/contracts/facets', + modulesOutputDir: 'website/docs/contracts/modules', + + // Template settings + defaultSidebarPosition: 99, + + // GitHub Models API settings (for optional AI enhancement) + // Uses Azure AI inference endpoint with GitHub token auth in Actions + // See: https://github.blog/changelog/2025-04-14-github-actions-token-integration-now-generally-available-in-github-models/ + models: { + host: 'models.inference.ai.azure.com', + model: 'gpt-4o', + maxTokens: 2000, + }, +}; + diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js new file mode 100644 index 00000000..185facbd --- /dev/null +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -0,0 +1,288 @@ +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); +const { readFileSafe } = require('../workflow-utils'); +const CONFIG = require('./config'); + +/** + * Get list of changed Solidity files from git diff + * @param {string} baseBranch - Base branch to compare against + * @returns {string[]} Array of changed .sol file paths + */ +function getChangedSolFiles(baseBranch = 'HEAD~1') { + try { + const output = execSync(`git diff --name-only ${baseBranch} HEAD -- 'src/**/*.sol'`, { + encoding: 'utf8', + }); + return output.trim().split('\n').filter(f => f.endsWith('.sol')); + } catch (error) { + console.error('Error getting changed files:', error.message); + return []; + } +} + +/** + * Get all Solidity files in src directory + * @returns {string[]} Array of .sol file paths + */ +function getAllSolFiles() { + try { + const output = execSync('find src -name "*.sol" -type f', { + encoding: 'utf8', + }); + return output.trim().split('\n').filter(f => f); + } catch (error) { + console.error('Error getting all sol files:', error.message); + return []; + } +} + +/** + * Find forge doc output files for a given source file + * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/LibAccessControl.sol') + * @returns {string[]} Array of markdown file paths from forge doc output + */ +function findForgeDocFiles(solFilePath) { + // Transform: src/access/AccessControl/LibAccessControl.sol + // To: docs/src/src/access/AccessControl/LibAccessControl.sol/ + const relativePath = solFilePath.replace(/^src\//, ''); + const docsDir = path.join(CONFIG.forgeDocsDir, relativePath); + + if (!fs.existsSync(docsDir)) { + return []; + } + + try { + const files = fs.readdirSync(docsDir); + return files + .filter(f => f.endsWith('.md')) + .map(f => path.join(docsDir, f)); + } catch (error) { + console.error(`Error reading docs dir ${docsDir}:`, error.message); + return []; + } +} + +/** + * Determine if a contract is an interface + * Interfaces should be skipped from documentation generation + * @param {string} title - Contract title/name + * @param {string} content - File content (forge doc markdown) + * @returns {boolean} True if this is an interface + */ +function isInterface(title, content) { + // Check if title follows interface naming convention: starts with "I" followed by uppercase + if (title && /^I[A-Z]/.test(title)) { + return true; + } + + // Check if content indicates it's an interface + // Forge doc marks interfaces with "interface" in the first few lines + if (content) { + const firstLines = content.split('\n').slice(0, 20).join('\n').toLowerCase(); + if (firstLines.includes('interface ') || firstLines.includes('*interface*')) { + return true; + } + } + + return false; +} + +/** + * Extract module name from file path + * @param {string} filePath - Path to the file (e.g., 'src/modules/LibNonReentrancy.sol' or 'constants.LibNonReentrancy.md') + * @returns {string} Module name (e.g., 'LibNonReentrancy') + */ +function extractModuleNameFromPath(filePath) { + const path = require('path'); + + // If it's a constants file, extract from filename + const basename = path.basename(filePath); + if (basename.startsWith('constants.')) { + const match = basename.match(/^constants\.(.+)\.md$/); + if (match) { + return match[1]; + } + } + + // Extract from .sol file path + if (filePath.endsWith('.sol')) { + return path.basename(filePath, '.sol'); + } + + // Extract from directory structure (e.g., docs/src/src/libraries/LibNonReentrancy.sol/function.enter.md) + const parts = filePath.split(path.sep); + for (let i = parts.length - 1; i >= 0; i--) { + if (parts[i].endsWith('.sol')) { + return path.basename(parts[i], '.sol'); + } + } + + // Fallback: use basename without extension + return path.basename(filePath, path.extname(filePath)); +} + +/** + * Extract module description from source file NatSpec comments + * @param {string} solFilePath - Path to the Solidity source file + * @returns {string} Description extracted from @title and @notice tags + */ +function extractModuleDescriptionFromSource(solFilePath) { + const content = readFileSafe(solFilePath); + if (!content) { + return ''; + } + + const lines = content.split('\n'); + let inComment = false; + let commentBuffer = []; + let title = ''; + let notice = ''; + let foundFirstCodeElement = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + // Skip SPDX and pragma lines + if (trimmed.startsWith('// SPDX') || trimmed.startsWith('pragma ')) { + continue; + } + + // Check if we've reached the first function/constant/error (end of file-level comments) + if (trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && !trimmed.startsWith('*') && + (trimmed.startsWith('function ') || trimmed.startsWith('error ') || trimmed.startsWith('event ') || + trimmed.startsWith('struct ') || trimmed.startsWith('enum ') || trimmed.match(/^\w+\s+constant/))) { + foundFirstCodeElement = true; + // If we're in a comment, finish processing it first + if (inComment) { + // Process the comment we were collecting + const commentText = commentBuffer.join(' '); + const titleMatch = commentText.match(/@title\s+(.+?)(?:\s+@|\s*\*\/|$)/); + if (titleMatch) { + title = titleMatch[1].trim(); + } + const noticeMatch = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*\*\/|$)/); + if (noticeMatch) { + notice = noticeMatch[1].trim(); + } + } + break; + } + + // Start of block comment + if (trimmed.startsWith('/*')) { + inComment = true; + commentBuffer = []; + continue; + } + + // End of block comment + if (inComment && trimmed.includes('*/')) { + inComment = false; + const commentText = commentBuffer.join(' '); + + // Extract @title + const titleMatch = commentText.match(/@title\s+(.+?)(?:\s+@|\s*\*\/|$)/); + if (titleMatch) { + title = titleMatch[1].trim(); + } + + // Extract @notice + const noticeMatch = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*\*\/|$)/); + if (noticeMatch) { + notice = noticeMatch[1].trim(); + } + + commentBuffer = []; + continue; + } + + // Collect comment lines + if (inComment) { + // Remove comment markers + let cleanLine = trimmed.replace(/^\*\s*/, '').replace(/^\s*\*/, '').trim(); + if (cleanLine && !cleanLine.startsWith('*/')) { + commentBuffer.push(cleanLine); + } + } + } + + // Combine title and notice + if (title && notice) { + return `${title} - ${notice}`; + } else if (notice) { + return notice; + } else if (title) { + return title; + } + + return ''; +} + +/** + * Determine if a contract is a module or facet + * Modules are Solidity files whose top-level code lives outside of contracts and Solidity libraries. + * They contain reusable logic that gets pulled into other contracts at compile time. + * @param {string} filePath - Path to the file + * @param {string} content - File content + * @returns {'module' | 'facet'} Contract type + */ +function getContractType(filePath, content) { + const lowerPath = filePath.toLowerCase(); + + // Check path patterns - files with 'lib' in the path are modules + if (lowerPath.includes('lib')) { + return 'module'; + } + + if (lowerPath.includes('facet')) { + return 'facet'; + } + + // Check content patterns - files with 'library' keyword (Solidity libraries) are modules + if (content && content.includes('library ')) { + return 'module'; + } + + // Default to facet for contracts + return 'facet'; +} + +/** + * Get output directory based on contract type + * @param {'module' | 'facet'} contractType - Type of contract + * @returns {string} Output directory path + */ +function getOutputDir(contractType) { + return contractType === 'module' + ? CONFIG.modulesOutputDir + : CONFIG.facetsOutputDir; +} + +/** + * Read changed files from a file (used in CI) + * @param {string} filePath - Path to file containing list of changed files + * @returns {string[]} Array of file paths + */ +function readChangedFilesFromFile(filePath) { + const content = readFileSafe(filePath); + if (!content) { + return []; + } + return content.trim().split('\n').filter(f => f.endsWith('.sol')); +} + +module.exports = { + getChangedSolFiles, + getAllSolFiles, + findForgeDocFiles, + isInterface, + getContractType, + getOutputDir, + readChangedFilesFromFile, + extractModuleNameFromPath, + extractModuleDescriptionFromSource, +}; + + diff --git a/.github/scripts/generate-docs-utils/forge-doc-parser.js b/.github/scripts/generate-docs-utils/forge-doc-parser.js new file mode 100644 index 00000000..4a79978a --- /dev/null +++ b/.github/scripts/generate-docs-utils/forge-doc-parser.js @@ -0,0 +1,686 @@ +/** + * Parser for forge doc markdown output + * Extracts structured data from forge-generated markdown files + */ + +/** + * Parse forge doc markdown output into structured data + * @param {string} content - Markdown content from forge doc + * @param {string} filePath - Path to the markdown file + * @returns {object} Parsed documentation data + */ +function parseForgeDocMarkdown(content, filePath) { + const data = { + title: '', + description: '', + subtitle: '', + overview: '', + gitSource: '', + functions: [], + events: [], + errors: [], + structs: [], + stateVariables: [], + }; + + const lines = content.split('\n'); + let currentSection = null; + let currentItem = null; + let itemType = null; + let collectingDescription = false; + let descriptionBuffer = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmedLine = line.trim(); + + // Parse title (# heading) + if (line.startsWith('# ') && !data.title) { + data.title = line.replace('# ', '').trim(); + continue; + } + + // Parse git source link + if (trimmedLine.startsWith('[Git Source]')) { + const match = trimmedLine.match(/\[Git Source\]\((.*?)\)/); + if (match) { + data.gitSource = match[1]; + } + continue; + } + + // Parse description (first non-empty lines after title, before sections) + if (data.title && !currentSection && trimmedLine && !line.startsWith('#') && !line.startsWith('[')) { + const sanitizedLine = sanitizeBrokenLinks(trimmedLine); + if (!data.description) { + data.description = sanitizedLine; + data.subtitle = sanitizedLine; + } else if (!data.overview) { + // Capture additional lines as overview + data.overview = data.description + '\n\n' + sanitizedLine; + } + continue; + } + + // Parse main sections + if (line.startsWith('## ')) { + const sectionName = line.replace('## ', '').trim().toLowerCase(); + + // Save current item before switching sections + if (currentItem) { + saveCurrentItem(data, currentItem, itemType); + currentItem = null; + itemType = null; + } + + if (sectionName === 'functions') { + currentSection = 'functions'; + } else if (sectionName === 'events') { + currentSection = 'events'; + } else if (sectionName === 'errors') { + currentSection = 'errors'; + } else if (sectionName === 'structs') { + currentSection = 'structs'; + } else if (sectionName === 'state variables') { + currentSection = 'stateVariables'; + } else { + currentSection = null; + } + continue; + } + + // Parse item definitions (### heading) + if (line.startsWith('### ') && currentSection) { + // Save previous item + if (currentItem) { + saveCurrentItem(data, currentItem, itemType); + } + + const name = line.replace('### ', '').trim(); + itemType = currentSection; + currentItem = createNewItem(name, currentSection); + collectingDescription = true; + descriptionBuffer = []; + continue; + } + + // Process content within current item + if (currentItem) { + // Code block with signature + if (line.startsWith('```solidity')) { + const codeLines = []; + i++; + while (i < lines.length && !lines[i].startsWith('```')) { + codeLines.push(lines[i]); + i++; + } + const codeContent = codeLines.join('\n').trim(); + + if (currentSection === 'functions' || currentSection === 'events' || currentSection === 'errors') { + currentItem.signature = codeContent; + + // Extract mutability from signature + if (codeContent.includes(' view ')) { + currentItem.mutability = 'view'; + } else if (codeContent.includes(' pure ')) { + currentItem.mutability = 'pure'; + } else if (codeContent.includes(' payable ')) { + currentItem.mutability = 'payable'; + } + } else if (currentSection === 'structs') { + currentItem.definition = codeContent; + } + continue; + } + + // Description text (before **Parameters** or **Returns**) + if (collectingDescription && trimmedLine && !trimmedLine.startsWith('**') && !trimmedLine.startsWith('|')) { + descriptionBuffer.push(trimmedLine); + continue; + } + + // End description collection on special markers + if (trimmedLine.startsWith('**Parameters**') || trimmedLine.startsWith('**Returns**')) { + if (descriptionBuffer.length > 0) { + const description = sanitizeBrokenLinks(descriptionBuffer.join(' ').trim()); + currentItem.description = description; + currentItem.notice = description; + descriptionBuffer = []; + } + collectingDescription = false; + } + + // Parse table rows (Parameters or Returns) + if (trimmedLine.startsWith('|') && !trimmedLine.includes('----')) { + const cells = trimmedLine.split('|').map(c => c.trim()).filter(c => c); + + // Skip header row + if (cells.length >= 3 && cells[0] !== 'Name' && cells[0] !== 'Parameter') { + const paramName = cells[0].replace(/`/g, '').trim(); + const paramType = cells[1].replace(/`/g, '').trim(); + const paramDesc = sanitizeBrokenLinks(cells[2] || ''); + + // Skip if parameter name matches the function name (parsing error) + if (currentItem && paramName === currentItem.name) { + continue; + } + + // Determine if Parameters or Returns based on preceding lines + const precedingLines = lines.slice(Math.max(0, i - 10), i).join('\n'); + + if (precedingLines.includes('**Returns**')) { + currentItem.returns = currentItem.returns || []; + currentItem.returns.push({ + name: paramName === '' ? '' : paramName, + type: paramType, + description: paramDesc, + }); + } else if (precedingLines.includes('**Parameters**')) { + // Only add if it looks like a valid parameter (has a type or starts with underscore) + if (paramType || paramName.startsWith('_')) { + currentItem.params = currentItem.params || []; + currentItem.params.push({ + name: paramName, + type: paramType, + description: paramDesc, + }); + } + } + } + } + } + } + + // Save last item + if (currentItem) { + saveCurrentItem(data, currentItem, itemType); + } + + // Ensure overview is set + if (!data.overview) { + data.overview = data.description || `Documentation for ${data.title}.`; + } + + return data; +} + +/** + * Create a new item object based on section type + * @param {string} name - Item name + * @param {string} section - Section type + * @returns {object} New item object + */ +function createNewItem(name, section) { + const base = { + name, + description: '', + notice: '', + }; + + switch (section) { + case 'functions': + return { + ...base, + signature: '', + params: [], + returns: [], + mutability: 'nonpayable', + }; + case 'events': + return { + ...base, + signature: '', + params: [], + }; + case 'errors': + return { + ...base, + signature: '', + params: [], + }; + case 'structs': + return { + ...base, + definition: '', + fields: [], + }; + case 'stateVariables': + return { + ...base, + type: '', + value: '', + }; + default: + return base; + } +} + +/** + * Save current item to data object + * @param {object} data - Data object to save to + * @param {object} item - Item to save + * @param {string} type - Item type + */ +function saveCurrentItem(data, item, type) { + if (!type || !item) return; + + switch (type) { + case 'functions': + data.functions.push(item); + break; + case 'events': + data.events.push(item); + break; + case 'errors': + data.errors.push(item); + break; + case 'structs': + data.structs.push(item); + break; + case 'stateVariables': + data.stateVariables.push(item); + break; + } +} + +/** + * Sanitize markdown links that point to non-existent files + * Removes or converts broken links to plain text + * @param {string} text - Text that may contain markdown links + * @returns {string} Sanitized text + */ +function sanitizeBrokenLinks(text) { + if (!text) return text; + + // Remove markdown links that point to /src/ paths (forge doc links) + // Pattern: [text](/src/...) + return text.replace(/\[([^\]]+)\]\(\/src\/[^\)]+\)/g, '$1'); +} + +/** + * Extract storage information from parsed data + * @param {object} data - Parsed documentation data + * @returns {string | null} Storage information or null + */ +function extractStorageInfo(data) { + // Look for STORAGE_POSITION in state variables + const storageVar = data.stateVariables.find(v => + v.name.includes('STORAGE') || v.name.includes('storage') + ); + + if (storageVar) { + return `Storage position: \`${storageVar.name}\` - ${storageVar.description || 'Used for diamond storage pattern.'}`; + } + + // Look for storage struct + const storageStruct = data.structs.find(s => + s.name.includes('Storage') + ); + + if (storageStruct) { + return `Uses the \`${storageStruct.name}\` struct following the ERC-8042 diamond storage pattern.`; + } + + return null; +} + +/** + * Detect item type from filename + * @param {string} filePath - Path to the markdown file + * @returns {string | null} Item type ('function', 'error', 'struct', 'event', 'enum', 'constants', or null) + */ +function detectItemTypeFromFilename(filePath) { + const basename = require('path').basename(filePath); + + if (basename.startsWith('function.')) return 'function'; + if (basename.startsWith('error.')) return 'error'; + if (basename.startsWith('struct.')) return 'struct'; + if (basename.startsWith('event.')) return 'event'; + if (basename.startsWith('enum.')) return 'enum'; + if (basename.startsWith('constants.')) return 'constants'; + + return null; +} + +/** + * Parse an individual item file (function, error, constant, etc.) + * @param {string} content - Markdown content from forge doc + * @param {string} filePath - Path to the markdown file + * @returns {object | null} Parsed item object or null if parsing fails + */ +function parseIndividualItemFile(content, filePath) { + const itemType = detectItemTypeFromFilename(filePath); + if (!itemType) { + return null; + } + + const lines = content.split('\n'); + let itemName = ''; + let gitSource = ''; + let description = ''; + let signature = ''; + let definition = ''; + let descriptionBuffer = []; + let inCodeBlock = false; + let codeBlockLines = []; + let params = []; + let returns = []; + let constants = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmedLine = line.trim(); + + // Parse title (# heading) + if (line.startsWith('# ') && !itemName) { + itemName = line.replace('# ', '').trim(); + continue; + } + + // Parse git source link + if (trimmedLine.startsWith('[Git Source]')) { + const match = trimmedLine.match(/\[Git Source\]\((.*?)\)/); + if (match) { + gitSource = match[1]; + } + continue; + } + + // Parse code block + if (line.startsWith('```solidity')) { + inCodeBlock = true; + codeBlockLines = []; + i++; + while (i < lines.length && !lines[i].startsWith('```')) { + codeBlockLines.push(lines[i]); + i++; + } + const codeContent = codeBlockLines.join('\n').trim(); + + if (itemType === 'constants') { + // For constants, parse multiple constant definitions + // Format: "bytes32 constant NON_REENTRANT_SLOT = keccak256(...)" + // Handle both single and multiple constants in one code block + const constantMatches = codeContent.match(/(\w+(?:\s*\d+)?)\s+constant\s+(\w+)\s*=\s*(.+?)(?:\s*;)?/g); + if (constantMatches) { + for (const match of constantMatches) { + const parts = match.match(/(\w+(?:\s*\d+)?)\s+constant\s+(\w+)\s*=\s*(.+?)(?:\s*;)?$/); + if (parts) { + constants.push({ + name: parts[2], + type: parts[1], + value: parts[3].trim(), + description: descriptionBuffer.join(' ').trim(), + }); + } + } + } else { + // Single constant definition (more flexible regex) + const singleMatch = codeContent.match(/(\w+(?:\s*\d+)?)\s+constant\s+(\w+)\s*=\s*(.+?)(?:\s*;)?$/); + if (singleMatch) { + constants.push({ + name: singleMatch[2], + type: singleMatch[1], + value: singleMatch[3].trim(), + description: descriptionBuffer.join(' ').trim(), + }); + } + } + // Clear description buffer after processing constants + descriptionBuffer = []; + } else { + signature = codeContent; + } + inCodeBlock = false; + continue; + } + + // Parse constants with ### heading format + if (itemType === 'constants' && line.startsWith('### ')) { + const constantName = line.replace('### ', '').trim(); + // Clear description buffer for this constant (only text before this heading) + // Filter out code block delimiters and empty lines + const currentConstantDesc = descriptionBuffer + .filter(l => l && !l.trim().startsWith('```') && l.trim() !== '') + .join(' ') + .trim(); + descriptionBuffer = []; + + // Look ahead for code block (within next 15 lines) + let foundCodeBlock = false; + let codeBlockEndIndex = i; + for (let j = i + 1; j < lines.length && j < i + 15; j++) { + if (lines[j].startsWith('```solidity')) { + foundCodeBlock = true; + const constCodeLines = []; + j++; + while (j < lines.length && !lines[j].startsWith('```')) { + constCodeLines.push(lines[j]); + j++; + } + codeBlockEndIndex = j; // j now points to the line after closing ``` + const constCode = constCodeLines.join('\n').trim(); + // Match: type constant name = value + // Handle complex types like "bytes32", "uint256", etc. + const constMatch = constCode.match(/(\w+(?:\s*\d+)?)\s+constant\s+(\w+)\s*=\s*(.+?)(?:\s*;)?$/); + if (constMatch) { + constants.push({ + name: constantName, + type: constMatch[1], + value: constMatch[3].trim(), + description: currentConstantDesc, + }); + } else { + // Fallback: if no match, still add constant with name from heading + constants.push({ + name: constantName, + type: '', + value: constCode, + description: currentConstantDesc, + }); + } + break; + } + } + if (!foundCodeBlock) { + // No code block found, but we have a heading - might be a constant without definition + // This shouldn't happen in forge doc output, but handle it gracefully + constants.push({ + name: constantName, + type: '', + value: '', + description: currentConstantDesc, + }); + } else { + // Skip to the end of the code block (the loop will increment i, so we set it to one before) + i = codeBlockEndIndex - 1; + } + continue; + } + + // Collect description (text before code block or after title) + // Skip code block delimiters, empty lines, and markdown table separators + if (!inCodeBlock && trimmedLine && + !trimmedLine.startsWith('#') && + !trimmedLine.startsWith('[') && + !trimmedLine.startsWith('|') && + !trimmedLine.startsWith('```') && + trimmedLine !== '') { + if (itemType !== 'constants' || !line.startsWith('###')) { + descriptionBuffer.push(trimmedLine); + } + continue; + } + + // Parse table rows (Parameters or Returns) + if (trimmedLine.startsWith('|') && !trimmedLine.includes('----')) { + const cells = trimmedLine.split('|').map(c => c.trim()).filter(c => c); + + if (cells.length >= 3 && cells[0] !== 'Name' && cells[0] !== 'Parameter') { + const paramName = cells[0].replace(/`/g, '').trim(); + const paramType = cells[1].replace(/`/g, '').trim(); + const paramDesc = sanitizeBrokenLinks(cells[2] || ''); + + // Determine if Parameters or Returns based on preceding lines + const precedingLines = lines.slice(Math.max(0, i - 10), i).join('\n'); + + if (precedingLines.includes('**Returns**')) { + returns.push({ + name: paramName === '' ? '' : paramName, + type: paramType, + description: paramDesc, + }); + } else if (precedingLines.includes('**Parameters**')) { + if (paramType || paramName.startsWith('_')) { + params.push({ + name: paramName, + type: paramType, + description: paramDesc, + }); + } + } + } + } + } + + // Combine description buffer + if (descriptionBuffer.length > 0) { + description = sanitizeBrokenLinks(descriptionBuffer.join(' ').trim()); + } + + // For constants, return array of constant objects + if (itemType === 'constants') { + return { + type: 'constants', + constants: constants.length > 0 ? constants : [{ + name: itemName || 'Constants', + type: '', + value: '', + description: description, + }], + gitSource: gitSource, + }; + } + + // For structs, use definition instead of signature + if (itemType === 'struct') { + definition = signature; + signature = ''; + } + + // Create item object based on type + const item = { + name: itemName, + description: description, + notice: description, + signature: signature, + definition: definition, + params: params, + returns: returns, + gitSource: gitSource, + }; + + // Add mutability for functions + if (itemType === 'function' && signature) { + if (signature.includes(' view ')) { + item.mutability = 'view'; + } else if (signature.includes(' pure ')) { + item.mutability = 'pure'; + } else if (signature.includes(' payable ')) { + item.mutability = 'payable'; + } else { + item.mutability = 'nonpayable'; + } + } + + return { + type: itemType, + item: item, + }; +} + +/** + * Aggregate multiple parsed items into a single data structure + * @param {Array} parsedItems - Array of parsed item objects from parseIndividualItemFile + * @param {string} sourceFilePath - Path to the source Solidity file + * @returns {object} Aggregated documentation data + */ +function aggregateParsedItems(parsedItems, sourceFilePath) { + const data = { + title: '', + description: '', + subtitle: '', + overview: '', + gitSource: '', + functions: [], + events: [], + errors: [], + structs: [], + stateVariables: [], + }; + + // Extract module name from source file path + const path = require('path'); + const basename = path.basename(sourceFilePath, '.sol'); + data.title = basename; + + // Extract git source from first item + for (const parsed of parsedItems) { + if (parsed && parsed.gitSource) { + data.gitSource = parsed.gitSource; + break; + } + } + + // Group items by type + for (const parsed of parsedItems) { + if (!parsed) continue; + + if (parsed.type === 'function' && parsed.item) { + data.functions.push(parsed.item); + } else if (parsed.type === 'error' && parsed.item) { + data.errors.push(parsed.item); + } else if (parsed.type === 'event' && parsed.item) { + data.events.push(parsed.item); + } else if (parsed.type === 'struct' && parsed.item) { + data.structs.push(parsed.item); + } else if (parsed.type === 'enum' && parsed.item) { + // Enums can be treated as structs for display purposes + data.structs.push(parsed.item); + } else if (parsed.type === 'constants' && parsed.constants) { + // Add constants as state variables + for (const constant of parsed.constants) { + data.stateVariables.push({ + name: constant.name, + type: constant.type, + value: constant.value, + description: constant.description, + }); + } + } + } + + // Set default description if not provided + // Don't use item descriptions as module description - they'll be overridden by source file parsing + if (!data.description || + data.description.includes('Event emitted') || + data.description.includes('Thrown when') || + data.description.includes('function to') || + data.description.length < 20) { + data.description = `Documentation for ${data.title}`; + data.subtitle = data.description; + data.overview = data.description; + } + + return data; +} + +module.exports = { + parseForgeDocMarkdown, + extractStorageInfo, + parseIndividualItemFile, + aggregateParsedItems, + detectItemTypeFromFilename, +}; + + diff --git a/.github/scripts/generate-docs-utils/pr-body-generator.js b/.github/scripts/generate-docs-utils/pr-body-generator.js new file mode 100644 index 00000000..a7e8a151 --- /dev/null +++ b/.github/scripts/generate-docs-utils/pr-body-generator.js @@ -0,0 +1,100 @@ +/** + * PR Body Generator + * + * Generates a PR body from the docgen-summary.json file + * + * Usage: + * node pr-body-generator.js [summary-file-path] + * + * Outputs the PR body in GitHub Actions format to stdout + */ + +const fs = require('fs'); +const path = require('path'); + +/** + * Generate PR body from summary data + * @param {Object} summary - Summary data from docgen-summary.json + * @returns {string} PR body markdown + */ +function generatePRBody(summary) { + const facets = summary.facets || []; + const libraries = summary.libraries || []; + const total = summary.totalGenerated || 0; + + let body = '## Auto-Generated API Documentation\n\n'; + body += 'This PR contains auto-generated documentation from contract comments using `forge doc`.\n\n'; + + body += '### Summary\n'; + body += `- **Total generated:** ${total} files\n\n`; + + if (facets.length > 0) { + body += '### Facets\n'; + facets.forEach(facet => { + body += `- ${facet.title}\n`; + }); + body += '\n'; + } + + if (libraries.length > 0) { + body += '### Libraries\n'; + libraries.forEach(lib => { + body += `- ${lib.title}\n`; + }); + body += '\n'; + } + + body += '### What was done\n'; + body += '1. Extracted NatSpec using `forge doc`\n'; + body += '2. Converted to Docusaurus MDX format\n'; + body += '3. Enhanced content with GitHub Copilot (optional)\n'; + body += '4. Verified documentation build\n\n'; + + body += '### Review Checklist\n'; + body += '- [ ] Review generated content for accuracy\n'; + body += '- [ ] Verify code examples are correct\n'; + body += '- [ ] Check for any missing documentation\n'; + body += '- [ ] Ensure consistency with existing docs\n\n'; + + body += '---\n'; + body += '* This PR was automatically generated. Please review before merging.*\n'; + + return body; +} + +/** + * Main function + */ +function main() { + const summaryPath = process.argv[2] || 'docgen-summary.json'; + + if (!fs.existsSync(summaryPath)) { + console.error(`Error: Summary file not found: ${summaryPath}`); + process.exit(1); + } + + try { + const summaryContent = fs.readFileSync(summaryPath, 'utf8'); + const summary = JSON.parse(summaryContent); + + const prBody = generatePRBody(summary); + + // Output in GitHub Actions format + console.log('body< + +{{/if}} +## Overview + +{{sanitizeMdx overview}} + +{{#if gitSource}} +[View Source]({{gitSource}}) + +{{/if}} +{{#if keyFeatures}} + +{{sanitizeMdx keyFeatures}} + + +{{/if}} +{{#if isModule}} + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +{{#if hasStorage}} +## Storage + +{{#if storageInfo}} +{{sanitizeMdx storageInfo}} + +{{/if}} +{{#if hasStateVariables}} +### State Variables + +{{#each stateVariables}} +#### {{name}} + +{{#if description}} +{{sanitizeMdx description}} + +{{/if}} +{{/each}} +{{/if}} +{{/if}} +{{/if}} +{{#if hasFunctions}} +## Functions + +{{#each functions}} +### {{name}} + +{{#if description}} +{{sanitizeMdx description}} + +{{/if}} +{{#if signature}} +```solidity +{{signature}} +``` + +{{/if}} +{{#if hasParams}} +**Parameters** + +| Name | Type | Description | +|------|------|-------------| +{{#each params}} +| `{{name}}` | `{{type}}` | {{escapeMarkdownTable description}} | +{{/each}} + +{{/if}} +{{#if hasReturns}} +**Returns** + +| Name | Type | Description | +|------|------|-------------| +{{#each returns}} +| `{{name}}` | `{{type}}` | {{escapeMarkdownTable description}} | +{{/each}} + +{{/if}} +{{/each}} +{{/if}} +{{#if hasEvents}} +## Events + +{{#each events}} +### {{name}} + +{{#if description}} +{{sanitizeMdx description}} + +{{/if}} +{{#if signature}} +```solidity +{{signature}} +``` + +{{/if}} +{{#if hasParams}} +| Parameter | Type | Description | +|-----------|------|-------------| +{{#each params}} +| `{{name}}` | `{{type}}` | {{escapeMarkdownTable description}} | +{{/each}} + +{{/if}} +{{/each}} +{{/if}} +{{#if hasErrors}} +## Errors + +{{#each errors}} +### {{name}} + +{{#if description}} +{{sanitizeMdx description}} + +{{/if}} +{{#if signature}} +```solidity +{{signature}} +``` + +{{/if}} +{{/each}} +{{/if}} +{{#if hasStructs}} +## Structs + +{{#each structs}} +### {{name}} + +{{#if description}} +{{sanitizeMdx description}} + +{{/if}} +{{#if definition}} +```solidity +{{definition}} +``` + +{{/if}} +{{/each}} +{{/if}} +{{#if usageExample}} +## Usage Example + +```solidity +{{usageExample}} +``` + +{{/if}} +{{#if bestPractices}} +## Best Practices + + +{{sanitizeMdx bestPractices}} + + +{{/if}} +{{#if isFacet}} +{{#if securityConsiderations}} +## Security Considerations + + +{{sanitizeMdx securityConsiderations}} + + +{{/if}} +{{/if}} +{{#if isModule}} +{{#if integrationNotes}} +## Integration Notes + + +{{sanitizeMdx integrationNotes}} + + +{{/if}} +{{/if}} + diff --git a/.github/scripts/generate-docs-utils/templates/helpers.js b/.github/scripts/generate-docs-utils/templates/helpers.js new file mode 100644 index 00000000..6b333657 --- /dev/null +++ b/.github/scripts/generate-docs-utils/templates/helpers.js @@ -0,0 +1,115 @@ +/** + * Template Helper Functions + * + * Escape and sanitization utilities for MDX/JSX template generation. + * Used by both template-engine.js and templates.js + */ + +/** + * Escape special characters for YAML frontmatter + * @param {string} str - String to escape + * @returns {string} Escaped string safe for YAML + */ +function escapeYaml(str) { + if (!str) return ''; + return str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, ' ') + .trim(); +} + +/** + * Sanitize text to prevent MDX from interpreting it as JSX + * @param {string} str - String to sanitize + * @returns {string} Sanitized string + */ +function sanitizeForMdx(str) { + if (!str) return ''; + return str + .replace(//g, '>') + .replace(/\{/g, '{') + .replace(/\}/g, '}'); +} + +/** + * Convert object/array to a safe JavaScript expression for JSX attributes + * Returns the value wrapped in curly braces for direct use in JSX: {value} + * @param {*} obj - Value to convert + * @returns {string} JSX expression with curly braces: {JSON} + */ +function toJsxExpression(obj) { + if (obj == null) return '{null}'; + + try { + let jsonStr = JSON.stringify(obj); + // Ensure single line + jsonStr = jsonStr.replace(/[\n\r]/g, ' ').replace(/\s+/g, ' ').trim(); + // Verify it's valid JSON + JSON.parse(jsonStr); + // Return with JSX curly braces included + return `{${jsonStr}}`; + } catch (e) { + console.warn('Invalid JSON generated:', e.message); + return Array.isArray(obj) ? '{[]}' : '{{}}'; + } +} + +/** + * Escape special characters for JSX string attributes + * @param {string} str - String to escape + * @returns {string} Escaped string safe for JSX attributes + */ +function escapeJsx(str) { + if (!str) return ''; + + return sanitizeForMdx(str) + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/'/g, "\\'") + .replace(/\n/g, ' ') + .replace(/\{/g, '{') + .replace(/\}/g, '}') + .trim(); +} + +/** + * Escape markdown table special characters + * @param {string} str - String to escape + * @returns {string} Escaped string safe for markdown tables + */ +function escapeMarkdownTable(str) { + if (!str) return ''; + return str + .replace(/\|/g, '\\|') + .replace(/\n/g, ' ') + .replace(/\{/g, '{') + .replace(/\}/g, '}'); +} + +/** + * Escape HTML entities for safe display + * @param {string} str - String to escape + * @returns {string} HTML-escaped string + */ +function escapeHtml(str) { + if (!str) return ''; + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +module.exports = { + escapeYaml, + escapeJsx, + sanitizeForMdx, + sanitizeMdx: sanitizeForMdx, // Alias for template usage + toJsxExpression, + escapeMarkdownTable, + escapeHtml, +}; + diff --git a/.github/scripts/generate-docs-utils/templates/template-engine.js b/.github/scripts/generate-docs-utils/templates/template-engine.js new file mode 100644 index 00000000..b8e2ed22 --- /dev/null +++ b/.github/scripts/generate-docs-utils/templates/template-engine.js @@ -0,0 +1,366 @@ +/** + * Simple Template Engine + * + * A lightweight template engine with no external dependencies. + * + * Supports: + * - Variable substitution: {{variable}} (HTML escaped) + * - Unescaped output: {{{variable}}} (raw output) + * - Conditionals: {{#if variable}}...{{/if}} + * - Loops: {{#each array}}...{{/each}} + * - Helper functions: {{helperName variable}} or {{helperName(arg1, arg2)}} + * - Dot notation: {{object.property.nested}} + */ + +const fs = require('fs'); +const path = require('path'); + +// Import helpers from separate module +const helpers = require('./helpers'); +const { escapeHtml } = helpers; + +/** + * Get value from object using dot notation path + * @param {object} obj - Object to get value from + * @param {string} dotPath - Dot notation path (e.g., "user.name") + * @returns {*} Value at path or undefined + */ +function getValue(obj, dotPath) { + if (!dotPath || !obj) return undefined; + + const parts = dotPath.split('.'); + let value = obj; + + for (const part of parts) { + if (value == null) return undefined; + value = value[part]; + } + + return value; +} + +/** + * Check if a value is truthy for template conditionals + * - null/undefined → false + * - empty array → false + * - empty object → false + * - empty string → false + * - false → false + * - everything else → true + * + * @param {*} value - Value to check + * @returns {boolean} Whether value is truthy + */ +function isTruthy(value) { + if (value == null) return false; + if (Array.isArray(value)) return value.length > 0; + if (typeof value === 'object') return Object.keys(value).length > 0; + if (typeof value === 'string') return value.trim().length > 0; + return Boolean(value); +} + +/** + * Process a helper function call + * @param {string} helperName - Name of the helper + * @param {string[]} args - Argument strings (variable paths or literals) + * @param {object} context - Current template context + * @param {object} helperRegistry - Registry of helper functions + * @returns {string} Result of helper function + */ +function processHelper(helperName, args, context, helperRegistry) { + const helper = helperRegistry[helperName]; + if (!helper) { + console.warn(`Unknown template helper: ${helperName}`); + return ''; + } + + // Process arguments - can be variable paths or quoted literals + const processedArgs = args.map(arg => { + arg = arg.trim(); + // Check for quoted literal strings + if ((arg.startsWith('"') && arg.endsWith('"')) || + (arg.startsWith("'") && arg.endsWith("'"))) { + return arg.slice(1, -1); + } + // Otherwise treat as variable path + return getValue(context, arg); + }); + + return helper(...processedArgs); +} + +/** + * Process a variable expression (helper or simple variable) + * @param {string} expression - The expression inside {{ }} + * @param {object} context - Current template context + * @param {boolean} escapeOutput - Whether to HTML-escape the output + * @param {object} helperRegistry - Registry of helper functions + * @returns {string} Processed value + */ +function processExpression(expression, context, escapeOutput, helperRegistry) { + const expr = expression.trim(); + + // Check for helper with parentheses: helperName(arg1, arg2) + const parenMatch = expr.match(/^(\w+)\((.*)\)$/); + if (parenMatch) { + const [, helperName, argsStr] = parenMatch; + const args = argsStr ? argsStr.split(',').map(a => a.trim()) : []; + return processHelper(helperName, args, context, helperRegistry); + } + + // Check for helper with space: helperName variable + const spaceMatch = expr.match(/^(\w+)\s+(.+)$/); + if (spaceMatch && helperRegistry[spaceMatch[1]]) { + const [, helperName, arg] = spaceMatch; + return processHelper(helperName, [arg], context, helperRegistry); + } + + // Regular variable lookup + const value = getValue(context, expr); + if (value == null) return ''; + + const str = String(value); + return escapeOutput ? escapeHtml(str) : str; +} + +/** + * Find the matching closing tag for a block, handling nesting + * @param {string} content - Content to search + * @param {string} openTag - Opening tag pattern (e.g., '#if', '#each') + * @param {string} closeTag - Closing tag (e.g., '/if', '/each') + * @param {number} startPos - Position after the opening tag + * @returns {number} Position of the matching closing tag, or -1 if not found + */ +function findMatchingClose(content, openTag, closeTag, startPos) { + let depth = 1; + let pos = startPos; + + const openPattern = new RegExp(`\\{\\{${openTag}\\s+[^}]+\\}\\}`, 'g'); + const closePattern = new RegExp(`\\{\\{${closeTag}\\}\\}`, 'g'); + + while (depth > 0 && pos < content.length) { + // Find next open and close tags + openPattern.lastIndex = pos; + closePattern.lastIndex = pos; + + const openMatch = openPattern.exec(content); + const closeMatch = closePattern.exec(content); + + if (!closeMatch) { + return -1; // No matching close found + } + + // If open comes before close, increase depth + if (openMatch && openMatch.index < closeMatch.index) { + depth++; + pos = openMatch.index + openMatch[0].length; + } else { + depth--; + if (depth === 0) { + return closeMatch.index; + } + pos = closeMatch.index + closeMatch[0].length; + } + } + + return -1; +} + +/** + * Process nested conditionals: {{#if variable}}...{{/if}} + * @param {string} content - Template content + * @param {object} context - Data context + * @param {object} helperRegistry - Registry of helper functions + * @returns {string} Processed content + */ +function processConditionals(content, context, helperRegistry) { + let result = content; + const openPattern = /\{\{#if\s+([^}]+)\}\}/g; + + let match; + while ((match = openPattern.exec(result)) !== null) { + const condition = match[1].trim(); + const startPos = match.index; + const afterOpen = startPos + match[0].length; + + const closePos = findMatchingClose(result, '#if', '/if', afterOpen); + if (closePos === -1) { + console.warn(`Unmatched {{#if ${condition}}} at position ${startPos}`); + break; + } + + const ifContent = result.substring(afterOpen, closePos); + const closeEndPos = closePos + '{{/if}}'.length; + + // Evaluate condition and get replacement + const value = getValue(context, condition); + const replacement = isTruthy(value) + ? processContent(ifContent, context, helperRegistry) + : ''; + + // Replace in result + result = result.substring(0, startPos) + replacement + result.substring(closeEndPos); + + // Reset pattern to start from beginning since we modified the string + openPattern.lastIndex = 0; + } + + return result; +} + +/** + * Process nested loops: {{#each array}}...{{/each}} + * @param {string} content - Template content + * @param {object} context - Data context + * @param {object} helperRegistry - Registry of helper functions + * @returns {string} Processed content + */ +function processLoops(content, context, helperRegistry) { + let result = content; + const openPattern = /\{\{#each\s+([^}]+)\}\}/g; + + let match; + while ((match = openPattern.exec(result)) !== null) { + const arrayPath = match[1].trim(); + const startPos = match.index; + const afterOpen = startPos + match[0].length; + + const closePos = findMatchingClose(result, '#each', '/each', afterOpen); + if (closePos === -1) { + console.warn(`Unmatched {{#each ${arrayPath}}} at position ${startPos}`); + break; + } + + const loopContent = result.substring(afterOpen, closePos); + const closeEndPos = closePos + '{{/each}}'.length; + + // Get array and process each item + const array = getValue(context, arrayPath); + let replacement = ''; + + if (Array.isArray(array) && array.length > 0) { + replacement = array.map((item, index) => { + const itemContext = { ...context, ...item, index }; + return processContent(loopContent, itemContext, helperRegistry); + }).join(''); + } + + // Replace in result + result = result.substring(0, startPos) + replacement + result.substring(closeEndPos); + + // Reset pattern to start from beginning since we modified the string + openPattern.lastIndex = 0; + } + + return result; +} + +/** + * Process template content with the given context + * Handles all variable substitutions, helpers, conditionals, and loops + * + * IMPORTANT: Processing order matters! + * 1. Loops first - so nested conditionals are evaluated with correct item context + * 2. Conditionals second - after loops have expanded their content + * 3. Variables last - after all control structures are resolved + * + * @param {string} content - Template content to process + * @param {object} context - Data context + * @param {object} helperRegistry - Registry of helper functions + * @returns {string} Processed content + */ +function processContent(content, context, helperRegistry) { + let result = content; + + // 1. Process loops FIRST (handles nesting properly) + result = processLoops(result, context, helperRegistry); + + // 2. Process conditionals SECOND (handles nesting properly) + result = processConditionals(result, context, helperRegistry); + + // 3. Process triple braces for unescaped output: {{{variable}}} + const tripleBracePattern = /\{\{\{([^}]+)\}\}\}/g; + result = result.replace(tripleBracePattern, (match, expr) => { + return processExpression(expr, context, false, helperRegistry); + }); + + // 4. Process double braces for escaped output: {{variable}} + const doubleBracePattern = /\{\{([^}]+)\}\}/g; + result = result.replace(doubleBracePattern, (match, expr) => { + return processExpression(expr, context, true, helperRegistry); + }); + + return result; +} + +/** + * Render a template string with data + * @param {string} template - Template string + * @param {object} data - Data to render + * @returns {string} Rendered template + */ +function renderTemplate(template, data) { + if (!template) return ''; + if (!data) data = {}; + + return processContent(template, { ...data }, helpers); +} + +/** + * List available template files + * @returns {string[]} Array of template names (without extension) + */ +function listAvailableTemplates() { + const templatesDir = path.join(__dirname, 'pages'); + try { + return fs.readdirSync(templatesDir) + .filter(f => f.endsWith('.mdx.template')) + .map(f => f.replace('.mdx.template', '')); + } catch (e) { + return []; + } +} + +/** + * Load and render a template file + * @param {string} templateName - Name of template (without extension) + * @param {object} data - Data to render + * @returns {string} Rendered template + * @throws {Error} If template cannot be loaded + */ +function loadAndRenderTemplate(templateName, data) { + console.log('Loading template:', templateName); + console.log('Data:', data); + + const templatePath = path.join(__dirname, 'pages', `${templateName}.mdx.template`); + + try { + if (!fs.existsSync(templatePath)) { + const available = listAvailableTemplates(); + throw new Error( + `Template '${templateName}' not found at: ${templatePath}\n` + + `Available templates: ${available.length > 0 ? available.join(', ') : 'none'}` + ); + } + + const template = fs.readFileSync(templatePath, 'utf8'); + return renderTemplate(template, data); + } catch (error) { + if (error.code === 'ENOENT') { + const available = listAvailableTemplates(); + throw new Error( + `Template file not found: ${templatePath}\n` + + `Available templates: ${available.length > 0 ? available.join(', ') : 'none'}` + ); + } + throw error; + } +} + +module.exports = { + renderTemplate, + loadAndRenderTemplate, + getValue, + isTruthy, + listAvailableTemplates, +}; diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js new file mode 100644 index 00000000..95b9ac16 --- /dev/null +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -0,0 +1,499 @@ +/** + * MDX Templates for Docusaurus documentation + * Uses template files with a simple template engine + */ + +const { loadAndRenderTemplate } = require('./template-engine'); +const { sanitizeForMdx } = require('./helpers'); +const { readFileSafe } = require('../../workflow-utils'); + +/** + * Extract function parameters directly from Solidity source file + * @param {string} sourceFilePath - Path to the Solidity source file + * @param {string} functionName - Name of the function to extract parameters from + * @returns {Array} Array of parameter objects with name and type + */ +function extractParamsFromSource(sourceFilePath, functionName) { + if (!sourceFilePath || !functionName) return []; + + const sourceContent = readFileSafe(sourceFilePath); + if (!sourceContent) { + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Could not read source file: ${sourceFilePath}`); + } + return []; + } + + // Remove comments to avoid parsing issues + const withoutComments = sourceContent + .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments + .replace(/\/\/.*$/gm, ''); // Remove line comments + + // Find function definition - match function name followed by opening parenthesis + // Handle both regular functions and free functions + const functionPattern = new RegExp( + `function\\s+${functionName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\(([^)]*)\\)`, + 's' + ); + + const match = withoutComments.match(functionPattern); + if (!match || !match[1]) { + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Function ${functionName} not found in source file`); + } + return []; + } + + const paramsStr = match[1].trim(); + if (!paramsStr) { + return []; // Function has no parameters + } + + // Parse parameters - handle complex types like mappings, arrays, structs + const params = []; + let currentParam = ''; + let depth = 0; + let inString = false; + let stringChar = ''; + + for (let i = 0; i < paramsStr.length; i++) { + const char = paramsStr[i]; + + // Handle string literals + if ((char === '"' || char === "'") && (i === 0 || paramsStr[i - 1] !== '\\')) { + if (!inString) { + inString = true; + stringChar = char; + } else if (char === stringChar) { + inString = false; + } + currentParam += char; + continue; + } + + if (inString) { + currentParam += char; + continue; + } + + // Track nesting depth for generics, arrays, mappings + if (char === '<' || char === '[' || char === '(') { + depth++; + currentParam += char; + } else if (char === '>' || char === ']' || char === ')') { + depth--; + currentParam += char; + } else if (char === ',' && depth === 0) { + // Found a parameter boundary + const trimmed = currentParam.trim(); + if (trimmed) { + const parsed = parseParameter(trimmed); + if (parsed) { + params.push(parsed); + } + } + currentParam = ''; + } else { + currentParam += char; + } + } + + // Handle last parameter + const trimmed = currentParam.trim(); + if (trimmed) { + const parsed = parseParameter(trimmed); + if (parsed) { + params.push(parsed); + } + } + + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Extracted ${params.length} params from source for ${functionName}:`, JSON.stringify(params, null, 2)); + } + + return params; +} + +/** + * Parse a single parameter string into name and type + * @param {string} paramStr - Parameter string (e.g., "uint256 amount" or "address") + * @returns {object|null} Object with name and type, or null if invalid + */ +function parseParameter(paramStr) { + if (!paramStr || !paramStr.trim()) return null; + + // Remove storage location keywords + const cleaned = paramStr + .replace(/\b(memory|storage|calldata)\b/g, '') + .replace(/\s+/g, ' ') + .trim(); + + // Split by whitespace - last token is usually the name, rest is type + const parts = cleaned.split(/\s+/); + + if (parts.length === 0) return null; + + // If only one part, it's just a type (unnamed parameter) + if (parts.length === 1) { + return { name: '', type: parts[0], description: '' }; + } + + // Last part is the name, everything before is the type + const name = parts[parts.length - 1]; + const type = parts.slice(0, -1).join(' '); + + // Validate: name should be a valid identifier + if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) { + // If name doesn't look valid, treat the whole thing as a type + return { name: '', type: cleaned, description: '' }; + } + + return { name, type, description: '' }; +} + +/** + * Extract parameters from function signature string + * @param {string} signature - Function signature string + * @returns {Array} Array of parameter objects with name and type + */ +function extractParamsFromSignature(signature) { + if (!signature || typeof signature !== 'string') return []; + + // Match function parameters: function name(params) or just (params) + const paramMatch = signature.match(/\(([^)]*)\)/); + if (!paramMatch || !paramMatch[1]) return []; + + const paramsStr = paramMatch[1].trim(); + if (!paramsStr) return []; + + // Split by comma, but be careful with nested generics + const params = []; + let currentParam = ''; + let depth = 0; + + for (let i = 0; i < paramsStr.length; i++) { + const char = paramsStr[i]; + if (char === '<') depth++; + else if (char === '>') depth--; + else if (char === ',' && depth === 0) { + const trimmed = currentParam.trim(); + if (trimmed) { + // Parse "type name" or just "type" + const parts = trimmed.split(/\s+/); + if (parts.length >= 2) { + // Has both type and name + const type = parts.slice(0, -1).join(' '); + const name = parts[parts.length - 1]; + params.push({ name, type, description: '' }); + } else if (parts.length === 1) { + // Just type, no name + params.push({ name: '', type: parts[0], description: '' }); + } + } + currentParam = ''; + continue; + } + currentParam += char; + } + + // Handle last parameter + const trimmed = currentParam.trim(); + if (trimmed) { + const parts = trimmed.split(/\s+/); + if (parts.length >= 2) { + const type = parts.slice(0, -1).join(' '); + const name = parts[parts.length - 1]; + params.push({ name, type, description: '' }); + } else if (parts.length === 1) { + params.push({ name: '', type: parts[0], description: '' }); + } + } + + return params; +} + +/** + * Filter function parameters, removing invalid entries + * Invalid parameters include: empty names or names matching the function name (parsing error) + * @param {Array} params - Raw parameters array + * @param {string} functionName - Name of the function (to detect parsing errors) + * @returns {Array} Filtered and normalized parameters + */ +function filterAndNormalizeParams(params, functionName) { + return (params || []) + .filter(p => { + // Handle different possible data structures + const paramName = (p && (p.name || p.param || p.parameter || '')).trim(); + const paramType = (p && (p.type || p.paramType || '')).trim(); + + // Filter out parameters with empty or missing names + if (!paramName) return false; + // Filter out parameters where name matches function name (indicates parsing error) + if (paramName === functionName) { + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Filtered out invalid param: name="${paramName}" matches function name`); + } + return false; + } + // Filter out if type is empty AND name looks like it might be a function name (starts with lowercase, no underscore) + if (!paramType && /^[a-z]/.test(paramName) && !paramName.includes('_')) { + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Filtered out suspicious param: name="${paramName}" has no type`); + } + return false; + } + return true; + }) + .map(p => ({ + name: (p.name || p.param || p.parameter || '').trim(), + type: (p.type || p.paramType || '').trim(), + description: (p.description || p.desc || '').trim(), + })); +} + +/** + * Prepare function data for template rendering (shared between facet and module) + * @param {object} fn - Function data + * @param {string} sourceFilePath - Path to the Solidity source file + * @param {boolean} useSourceExtraction - Whether to try extracting params from source file (for modules) + * @returns {object} Prepared function data + */ +function prepareFunctionData(fn, sourceFilePath, useSourceExtraction = false) { + // Debug: log the raw function data + if (process.env.DEBUG_PARAMS) { + console.log(`\n[DEBUG] Function: ${fn.name}`); + console.log(`[DEBUG] Raw params:`, JSON.stringify(fn.params, null, 2)); + console.log(`[DEBUG] Signature:`, fn.signature); + } + + // Build parameters array, filtering out invalid parameters + let paramsArray = filterAndNormalizeParams(fn.params, fn.name); + + // If no valid parameters found, try extracting from source file (for modules) or signature + if (paramsArray.length === 0) { + // Try source file extraction for modules + if (useSourceExtraction && sourceFilePath) { + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] No valid params found, extracting from source file: ${sourceFilePath}`); + } + const extractedParams = extractParamsFromSource(sourceFilePath, fn.name); + if (extractedParams.length > 0) { + paramsArray = extractedParams; + } + } + + // Fallback to signature extraction if still no params + if (paramsArray.length === 0 && fn.signature) { + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] No valid params found, extracting from signature`); + } + const extractedParams = extractParamsFromSignature(fn.signature); + paramsArray = filterAndNormalizeParams(extractedParams, fn.name); + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Extracted params from signature:`, JSON.stringify(paramsArray, null, 2)); + } + } + } + + if (process.env.DEBUG_PARAMS) { + console.log(`[DEBUG] Final paramsArray:`, JSON.stringify(paramsArray, null, 2)); + } + + // Build returns array for table rendering + const returnsArray = (fn.returns || []).map(r => ({ + name: r.name || '-', + type: r.type, + description: r.description || '', + })); + + return { + name: fn.name, + signature: fn.signature, + description: fn.notice || fn.description || '', + params: paramsArray, + returns: returnsArray, + hasReturns: returnsArray.length > 0, + hasParams: paramsArray.length > 0, + }; +} + +/** + * Prepare event data for template rendering + * @param {object} event - Event data + * @returns {object} Prepared event data + */ +function prepareEventData(event) { + return { + name: event.name, + description: event.description || '', + signature: event.signature, + params: (event.params || []).map(p => ({ + name: p.name, + type: p.type, + description: p.description || '', + })), + hasParams: (event.params || []).length > 0, + }; +} + +/** + * Prepare error data for template rendering + * @param {object} error - Error data + * @returns {object} Prepared error data + */ +function prepareErrorData(error) { + return { + name: error.name, + description: error.description || '', + signature: error.signature, + }; +} + +/** + * Prepare struct data for template rendering + * @param {object} struct - Struct data + * @returns {object} Prepared struct data + */ +function prepareStructData(struct) { + return { + name: struct.name, + description: struct.description || '', + definition: struct.definition, + }; +} + +/** + * Validate documentation data + * @param {object} data - Documentation data to validate + * @throws {Error} If data is invalid + */ +function validateData(data) { + if (!data || typeof data !== 'object') { + throw new Error('Invalid data: expected an object'); + } + if (!data.title || typeof data.title !== 'string') { + throw new Error('Invalid data: missing or invalid title'); + } +} + +/** + * Prepare base data common to both facet and module templates + * @param {object} data - Documentation data + * @param {number} position - Sidebar position + * @returns {object} Base prepared data + */ +function prepareBaseData(data, position = 99) { + validateData(data); + + const description = data.description || `Contract documentation for ${data.title}`; + const subtitle = data.subtitle || data.description || `Contract documentation for ${data.title}`; + const overview = data.overview || data.description || `Documentation for ${data.title}.`; + + return { + position, + title: data.title, + description, + subtitle, + overview, + gitSource: data.gitSource || '', + keyFeatures: data.keyFeatures || '', + usageExample: data.usageExample || '', + bestPractices: data.bestPractices || '', + securityConsiderations: data.securityConsiderations || '', + integrationNotes: data.integrationNotes || '', + storageInfo: data.storageInfo || '', + + // Events + events: (data.events || []).map(prepareEventData), + hasEvents: (data.events || []).length > 0, + + // Errors + errors: (data.errors || []).map(prepareErrorData), + hasErrors: (data.errors || []).length > 0, + + // Structs + structs: (data.structs || []).map(prepareStructData), + hasStructs: (data.structs || []).length > 0, + + // State variables (for modules) + stateVariables: (data.stateVariables || []).map(v => ({ + name: v.name, + description: v.description || '', + })), + hasStateVariables: (data.stateVariables || []).length > 0, + hasStorage: Boolean(data.storageInfo || (data.stateVariables && data.stateVariables.length > 0)), + }; +} + +/** + * Prepare data for facet template rendering + * @param {object} data - Documentation data + * @param {number} position - Sidebar position + * @returns {object} Prepared data for facet template + */ +function prepareFacetData(data, position = 99) { + const baseData = prepareBaseData(data, position); + const sourceFilePath = data.sourceFilePath; + + return { + ...baseData, + // Contract type flags for unified template + isFacet: true, + isModule: false, + contractType: 'facet', + // Functions with APIReference-compatible format (no source extraction for facets) + functions: (data.functions || []).map(fn => prepareFunctionData(fn, sourceFilePath, false)), + hasFunctions: (data.functions || []).length > 0, + }; +} + +/** + * Prepare data for module template rendering + * @param {object} data - Documentation data + * @param {number} position - Sidebar position + * @returns {object} Prepared data for module template + */ +function prepareModuleData(data, position = 99) { + const baseData = prepareBaseData(data, position); + const sourceFilePath = data.sourceFilePath; + + return { + ...baseData, + // Contract type flags for unified template + isFacet: false, + isModule: true, + contractType: 'module', + // Functions with table-compatible format (with source extraction for modules) + functions: (data.functions || []).map(fn => prepareFunctionData(fn, sourceFilePath, true)), + hasFunctions: (data.functions || []).length > 0, + }; +} + +/** + * Generate complete facet documentation + * Uses the unified contract template with isFacet=true + * @param {object} data - Documentation data + * @param {number} position - Sidebar position + * @returns {string} Complete MDX document + */ +function generateFacetDoc(data, position = 99) { + const preparedData = prepareFacetData(data, position); + return loadAndRenderTemplate('contract', preparedData); +} + +/** + * Generate complete module documentation + * Uses the unified contract template with isModule=true + * @param {object} data - Documentation data + * @param {number} position - Sidebar position + * @returns {string} Complete MDX document + */ +function generateModuleDoc(data, position = 99) { + const preparedData = prepareModuleData(data, position); + return loadAndRenderTemplate('contract', preparedData); +} + +module.exports = { + generateFacetDoc, + generateModuleDoc, +}; diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js new file mode 100644 index 00000000..68a61d25 --- /dev/null +++ b/.github/scripts/generate-docs.js @@ -0,0 +1,374 @@ +/** + * Docusaurus Documentation Generator + * + * Converts forge doc output to Docusaurus MDX format + * with optional GitHub Copilot enhancement. + * + * Environment variables: + * GITHUB_TOKEN - GitHub token for Copilot API (optional) + * SKIP_ENHANCEMENT - Set to 'true' to skip Copilot enhancement + */ + +const path = require('path'); +const { + getAllSolFiles, + findForgeDocFiles, + isInterface, + getContractType, + getOutputDir, + readChangedFilesFromFile, + extractModuleNameFromPath, + extractModuleDescriptionFromSource, +} = require('./generate-docs-utils/doc-generation-utils'); +const { readFileSafe, writeFileSafe } = require('./workflow-utils'); +const { + parseForgeDocMarkdown, + extractStorageInfo, + parseIndividualItemFile, + aggregateParsedItems, + detectItemTypeFromFilename, +} = require('./generate-docs-utils/forge-doc-parser'); +const { generateFacetDoc, generateModuleDoc } = require('./generate-docs-utils/templates/templates'); +const { enhanceWithAI, shouldSkipEnhancement, addFallbackContent } = require('./generate-docs-utils/ai-enhancement'); + +// Track processed files for summary +const processedFiles = { + facets: [], + modules: [], + skipped: [], + errors: [], +}; + +/** + * Process a single forge doc markdown file + * @param {string} forgeDocFile - Path to forge doc markdown file + * @param {string} solFilePath - Original .sol file path + * @returns {Promise} True if processed successfully + */ +async function processForgeDocFile(forgeDocFile, solFilePath) { + const content = readFileSafe(forgeDocFile); + if (!content) { + console.log(`Could not read: ${forgeDocFile}`); + processedFiles.errors.push({ file: forgeDocFile, error: 'Could not read file' }); + return false; + } + + // Parse the forge doc markdown + const data = parseForgeDocMarkdown(content, forgeDocFile); + + // Add source file path for parameter extraction + if (solFilePath) { + data.sourceFilePath = solFilePath; + } + + if (!data.title) { + console.log(`Could not parse title from: ${forgeDocFile}`); + processedFiles.skipped.push({ file: forgeDocFile, reason: 'No title found' }); + return false; + } + + // Skip interfaces - only generate docs for facets and modules + if (isInterface(data.title, content)) { + console.log(`Skipping interface: ${data.title}`); + processedFiles.skipped.push({ file: forgeDocFile, reason: 'Interface (filtered)' }); + return false; + } + + // Determine contract type + const contractType = getContractType(forgeDocFile, content); + console.log(`Type: ${contractType} - ${data.title}`); + + // Extract storage info for modules + if (contractType === 'module') { + data.storageInfo = extractStorageInfo(data); + } + + // Check if we should skip AI enhancement (e.g., for interfaces or when SKIP_ENHANCEMENT is set) + const skipAIEnhancement = shouldSkipEnhancement(data) || process.env.SKIP_ENHANCEMENT === 'true'; + + // Enhance with Copilot if not skipped, otherwise add fallback content + let enhancedData = data; + if (!skipAIEnhancement) { + const token = process.env.GITHUB_TOKEN; + enhancedData = await enhanceWithCopilot(data, contractType, token); + } else { + console.log(`Skipping AI enhancement for ${data.title}`); + // Add fallback content when skipping AI enhancement + const { addFallbackContent } = require('./generate-docs-utils/copilot-enhancement'); + enhancedData = addFallbackContent(data, contractType); + } + + const mdxContent = contractType === 'module' + ? generateModuleDoc(enhancedData) + : generateFacetDoc(enhancedData); + + const outputDir = getOutputDir(contractType); + const outputFile = path.join(outputDir, `${data.title}.mdx`); + + if (writeFileSafe(outputFile, mdxContent)) { + console.log('✅ Generated:', outputFile); + + if (contractType === 'module') { + processedFiles.modules.push({ title: data.title, file: outputFile }); + } else { + processedFiles.facets.push({ title: data.title, file: outputFile }); + } + + return true; + } + + processedFiles.errors.push({ file: outputFile, error: 'Could not write file' }); + return false; +} + +/** + * Check if files need aggregation (individual item files vs contract-level files) + * @param {string[]} forgeDocFiles - Array of forge doc file paths + * @returns {boolean} True if files are individual items that need aggregation + */ +function needsAggregation(forgeDocFiles) { + for (const file of forgeDocFiles) { + const itemType = detectItemTypeFromFilename(file); + if (itemType) { + return true; + } + } + return false; +} + +/** + * Process aggregated files (for free function modules) + * @param {string[]} forgeDocFiles - Array of forge doc file paths + * @param {string} solFilePath - Original .sol file path + * @returns {Promise} True if processed successfully + */ +async function processAggregatedFiles(forgeDocFiles, solFilePath) { + console.log(`Aggregating ${forgeDocFiles.length} files for: ${solFilePath}`); + + const parsedItems = []; + let gitSource = ''; + + for (const forgeDocFile of forgeDocFiles) { + const content = readFileSafe(forgeDocFile); + if (!content) { + console.log(`Could not read: ${forgeDocFile}`); + continue; + } + + console.log(`Reading: ${path.basename(forgeDocFile)}`); + const parsed = parseIndividualItemFile(content, forgeDocFile); + if (parsed) { + parsedItems.push(parsed); + if (parsed.gitSource && !gitSource) { + gitSource = parsed.gitSource; + } + } + } + + if (parsedItems.length === 0) { + console.log(`No valid items found in files for: ${solFilePath}`); + processedFiles.errors.push({ file: solFilePath, error: 'No valid items parsed' }); + return false; + } + + const data = aggregateParsedItems(parsedItems, solFilePath); + + data.sourceFilePath = solFilePath; + + if (!data.title) { + data.title = extractModuleNameFromPath(solFilePath); + } + + const sourceDescription = extractModuleDescriptionFromSource(solFilePath); + if (sourceDescription) { + data.description = sourceDescription; + data.subtitle = sourceDescription; + data.overview = sourceDescription; + } else { + // Use a generic description if no source description found + const genericDescription = `Module providing internal functions for ${data.title}`; + if (!data.description || data.description.includes('Event emitted') || data.description.includes('Thrown when')) { + data.description = genericDescription; + data.subtitle = genericDescription; + data.overview = genericDescription; + } + } + + if (gitSource) { + data.gitSource = gitSource; + } + + const contractType = getContractType(solFilePath, ''); + console.log(`Type: ${contractType} - ${data.title}`); + + if (contractType === 'module') { + data.storageInfo = extractStorageInfo(data); + } + + const skipAIEnhancement = shouldSkipEnhancement(data) || process.env.SKIP_ENHANCEMENT === 'true'; + + let enhancedData = data; + if (!skipAIEnhancement) { + const token = process.env.GITHUB_TOKEN; + enhancedData = await enhanceWithAI(data, contractType, token); + } else { + console.log(`Skipping AI enhancement for ${data.title}`); + // Add fallback content when skipping AI enhancement + enhancedData = addFallbackContent(data, contractType); + } + + // Generate MDX content + const mdxContent = contractType === 'module' + ? generateModuleDoc(enhancedData) + : generateFacetDoc(enhancedData); + + // Determine output path + const outputDir = getOutputDir(contractType); + const outputFile = path.join(outputDir, `${data.title}.mdx`); + + // Write the file + if (writeFileSafe(outputFile, mdxContent)) { + console.log('✅ Generated:', outputFile); + + if (contractType === 'module') { + processedFiles.modules.push({ title: data.title, file: outputFile }); + } else { + processedFiles.facets.push({ title: data.title, file: outputFile }); + } + + return true; + } + + processedFiles.errors.push({ file: outputFile, error: 'Could not write file' }); + return false; +} + +/** + * Process a Solidity source file + * @param {string} solFilePath - Path to .sol file + * @returns {Promise} + */ +async function processSolFile(solFilePath) { + console.log(`Processing: ${solFilePath}`); + + const forgeDocFiles = findForgeDocFiles(solFilePath); + + if (forgeDocFiles.length === 0) { + console.log(`No forge doc output found for: ${solFilePath}`); + processedFiles.skipped.push({ file: solFilePath, reason: 'No forge doc output' }); + return; + } + + if (needsAggregation(forgeDocFiles)) { + await processAggregatedFiles(forgeDocFiles, solFilePath); + } else { + for (const forgeDocFile of forgeDocFiles) { + console.log(`Reading: ${path.basename(forgeDocFile)}`); + await processForgeDocFile(forgeDocFile, solFilePath); + } + } +} + +/** + * Print processing summary + */ +function printSummary() { + console.log('\n' + '='.repeat(50)); + console.log('Documentation Generation Summary'); + console.log('='.repeat(50)); + + console.log(`\nFacets generated: ${processedFiles.facets.length}`); + for (const f of processedFiles.facets) { + console.log(` - ${f.title}`); + } + + console.log(`\nModules generated: ${processedFiles.modules.length}`); + for (const m of processedFiles.modules) { + console.log(` - ${m.title}`); + } + + if (processedFiles.skipped.length > 0) { + console.log(`\nSkipped: ${processedFiles.skipped.length}`); + for (const s of processedFiles.skipped) { + console.log(` - ${path.basename(s.file)}: ${s.reason}`); + } + } + + if (processedFiles.errors.length > 0) { + console.log(`\nErrors: ${processedFiles.errors.length}`); + for (const e of processedFiles.errors) { + console.log(` - ${path.basename(e.file)}: ${e.error}`); + } + } + + const total = processedFiles.facets.length + processedFiles.modules.length; + console.log(`\nTotal generated: ${total} documentation files`); + console.log('='.repeat(50) + '\n'); +} + +/** + * Write summary to file for GitHub Action + */ +function writeSummaryFile() { + const summary = { + timestamp: new Date().toISOString(), + facets: processedFiles.facets, + modules: processedFiles.modules, + skipped: processedFiles.skipped, + errors: processedFiles.errors, + totalGenerated: processedFiles.facets.length + processedFiles.modules.length, + }; + + writeFileSafe('docgen-summary.json', JSON.stringify(summary, null, 2)); +} + +/** + * Main entry point + */ +async function main() { + console.log('Compose Documentation Generator\n'); + + const args = process.argv.slice(2); + let solFiles = []; + + if (args.includes('--all')) { + console.log('Processing all Solidity files...'); + solFiles = getAllSolFiles(); + } else if (args.length > 0 && !args[0].startsWith('--')) { + const changedFilesPath = args[0]; + console.log(`Reading changed files from: ${changedFilesPath}`); + solFiles = readChangedFilesFromFile(changedFilesPath); + + if (solFiles.length === 0) { + console.log('No files in list, checking git diff...'); + const { getChangedSolFiles } = require('./generate-docs-utils/doc-generation-utils'); + solFiles = getChangedSolFiles(); + } + } else { + console.log('Getting changed Solidity files from git...'); + const { getChangedSolFiles } = require('./generate-docs-utils/doc-generation-utils'); + solFiles = getChangedSolFiles(); + } + + if (solFiles.length === 0) { + console.log('No Solidity files to process'); + return; + } + + console.log(`Found ${solFiles.length} Solidity file(s) to process\n`); + + for (const solFile of solFiles) { + await processSolFile(solFile); + console.log(''); + } + + printSummary(); + writeSummaryFile(); +} + +main().catch((error) => { + console.error(`Fatal error: ${error}`); + process.exit(1); +}); + + diff --git a/.github/scripts/workflow-utils.js b/.github/scripts/workflow-utils.js index 0a254309..d95956d7 100644 --- a/.github/scripts/workflow-utils.js +++ b/.github/scripts/workflow-utils.js @@ -1,4 +1,5 @@ const fs = require('fs'); +const https = require('https'); const path = require('path'); const { execSync } = require('child_process'); @@ -63,18 +64,69 @@ function parsePRNumber(dataFileName) { } /** - * Read report file + * Read file content safely + * @param {string} filePath - Path to file (absolute or relative to workspace) + * @returns {string|null} File content or null if error + */ +function readFileSafe(filePath) { + try { + // If relative path, join with workspace if available + const fullPath = process.env.GITHUB_WORKSPACE && !path.isAbsolute(filePath) + ? path.join(process.env.GITHUB_WORKSPACE, filePath) + : filePath; + + if (!fs.existsSync(fullPath)) { + return null; + } + + return fs.readFileSync(fullPath, 'utf8'); + } catch (error) { + console.error(`Error reading file ${filePath}:`, error.message); + return null; + } +} + +/** + * Read report file (legacy - use readFileSafe for new code) * @param {string} reportFileName - Name of the report file * @returns {string|null} Report content or null if not found */ function readReport(reportFileName) { const reportPath = path.join(process.env.GITHUB_WORKSPACE, reportFileName); + return readFileSafe(reportPath); +} - if (!fs.existsSync(reportPath)) { - return null; +/** + * Ensure directory exists, create if not + * @param {string} dirPath - Directory path + */ +function ensureDir(dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); } +} - return fs.readFileSync(reportPath, 'utf8'); +/** + * Write file safely + * @param {string} filePath - Path to file (absolute or relative to workspace) + * @param {string} content - Content to write + * @returns {boolean} True if successful + */ +function writeFileSafe(filePath, content) { + try { + // If relative path, join with workspace if available + const fullPath = process.env.GITHUB_WORKSPACE && !path.isAbsolute(filePath) + ? path.join(process.env.GITHUB_WORKSPACE, filePath) + : filePath; + + const dir = path.dirname(fullPath); + ensureDir(dir); + fs.writeFileSync(fullPath, content); + return true; + } catch (error) { + console.error(`Error writing file ${filePath}:`, error.message); + return false; + } } /** @@ -129,9 +181,61 @@ async function postOrUpdateComment(github, context, prNumber, body, commentMarke } } +/** + * Sleep for specified milliseconds + * @param {number} ms - Milliseconds to sleep + * @returns {Promise} + */ +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +/** + * Make HTTPS request (promisified) + * @param {object} options - Request options + * @param {string} body - Request body + * @returns {Promise} Response data + */ +function makeHttpsRequest(options, body) { + return new Promise((resolve, reject) => { + const req = https.request(options, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + if (res.statusCode >= 200 && res.statusCode < 300) { + try { + resolve(JSON.parse(data)); + } catch (e) { + resolve({ raw: data }); + } + } else { + reject(new Error(`HTTP ${res.statusCode}: ${data}`)); + } + }); + }); + + req.on('error', reject); + + if (body) { + req.write(body); + } + + req.end(); + }); +} + module.exports = { downloadArtifact, parsePRNumber, readReport, - postOrUpdateComment + readFileSafe, + writeFileSafe, + ensureDir, + postOrUpdateComment, + sleep, + makeHttpsRequest, }; \ No newline at end of file diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml new file mode 100644 index 00000000..dce0fc58 --- /dev/null +++ b/.github/workflows/generate-docs.yml @@ -0,0 +1,159 @@ +name: Generate Facets & Modules Docs + +on: + push: + branches: [main] + paths: + - 'src/**/*.sol' + workflow_dispatch: + inputs: + process_all: + description: 'Process ALL Solidity files' + required: false + default: false + type: boolean + skip_enhancement: + description: 'Skip AI Documentation Enhancement' + required: false + default: false + type: boolean + +permissions: + contents: write + pull-requests: write + models: read # Required for GitHub Models API (AI enhancement) + +jobs: + generate-docs: + name: Generate Pages + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history for git diff + submodules: recursive + + - name: Get changed Solidity files + id: changed-files + run: | + if [ "${{ github.event.inputs.process_all }}" == "true" ]; then + echo "Processing all Solidity files (manual trigger)" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "process_all=true" >> $GITHUB_OUTPUT + else + # Get list of changed .sol files compared to previous commit + CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD -- 'src/**/*.sol' 2>/dev/null || echo "") + + if [ -z "$CHANGED_FILES" ]; then + echo "No Solidity files changed" + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "Changed files:" + echo "$CHANGED_FILES" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "process_all=false" >> $GITHUB_OUTPUT + + # Save to file for script + echo "$CHANGED_FILES" > /tmp/changed_sol_files.txt + fi + fi + + - name: Setup Node.js + if: steps.changed-files.outputs.has_changes == 'true' + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Foundry + if: steps.changed-files.outputs.has_changes == 'true' + uses: foundry-rs/foundry-toolchain@v1 + + - name: Generate forge documentation + if: steps.changed-files.outputs.has_changes == 'true' + run: forge doc + + - name: Run documentation generator + if: steps.changed-files.outputs.has_changes == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SKIP_ENHANCEMENT: ${{ github.event.inputs.skip_enhancement || 'false' }} + run: | + if [ "${{ steps.changed-files.outputs.process_all }}" == "true" ]; then + node .github/scripts/generate-docs.js --all + else + node .github/scripts/generate-docs.js /tmp/changed_sol_files.txt + fi + + - name: Check for generated files + if: steps.changed-files.outputs.has_changes == 'true' + id: check-generated + run: | + # Check if any files were generated + if [ -f "docgen-summary.json" ]; then + TOTAL=$(cat docgen-summary.json | jq -r '.totalGenerated // 0' 2>/dev/null || echo "0") + if [ -n "$TOTAL" ] && [ "$TOTAL" -gt "0" ]; then + echo "has_generated=true" >> $GITHUB_OUTPUT + echo "Generated $TOTAL documentation files" + else + echo "has_generated=false" >> $GITHUB_OUTPUT + echo "No documentation files generated" + fi + else + echo "has_generated=false" >> $GITHUB_OUTPUT + fi + + - name: Verify documentation site build + if: steps.check-generated.outputs.has_generated == 'true' + working-directory: website + run: | + npm ci + npm run build + env: + # Prevent build failures from missing keys + ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID || 'dummy' }} + ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY || 'dummy' }} + ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME || 'compose' }} + POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY || 'dummy' }} + continue-on-error: true # TODO: Disable when ready to merge + + - name: Generate PR body + if: steps.check-generated.outputs.has_generated == 'true' + id: pr-body + run: | + node .github/scripts/generate-docs-utils/pr-body-generator.js docgen-summary.json >> $GITHUB_OUTPUT + + - name: Clean up tmp files and stage website pages + if: steps.check-generated.outputs.has_generated == 'true' + run: | + # Remove forge docs folder (if it exists) + if [ -d "docs" ]; then + rm -rf docs + fi + + # Remove summary file (if it exists) + if [ -f "docgen-summary.json" ]; then + rm -f docgen-summary.json + fi + + # Reset any staged changes + git reset + + # Only stage website documentation files + git add website/docs/contracts/ + + - name: Create Pull Request + if: steps.check-generated.outputs.has_generated == 'true' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + title: '[DOCS] Auto-generated API documentation' + commit-message: 'docs: auto-generate API docs from NatSpec' + branch: docs/auto-generated-${{ github.run_number }} + body: ${{ steps.pr-body.outputs.body }} + labels: | + documentation + auto-generated + delete-branch: true + draft: true From 12871fa6170b53e06cd47050499622e3bcc9f971 Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Sun, 7 Dec 2025 09:36:39 -0500 Subject: [PATCH 02/68] rename old ref --- .github/scripts/generate-docs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index 68a61d25..f9129cc6 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -86,11 +86,11 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { // Check if we should skip AI enhancement (e.g., for interfaces or when SKIP_ENHANCEMENT is set) const skipAIEnhancement = shouldSkipEnhancement(data) || process.env.SKIP_ENHANCEMENT === 'true'; - // Enhance with Copilot if not skipped, otherwise add fallback content + // Enhance with AI if not skipped, otherwise add fallback content let enhancedData = data; if (!skipAIEnhancement) { const token = process.env.GITHUB_TOKEN; - enhancedData = await enhanceWithCopilot(data, contractType, token); + enhancedData = await enhanceWithAI(data, contractType, token); } else { console.log(`Skipping AI enhancement for ${data.title}`); // Add fallback content when skipping AI enhancement From 7050baa250694d5e2445aa4c6b96c62c326cb1cb Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Sun, 7 Dec 2025 09:43:55 -0500 Subject: [PATCH 03/68] readd pages folder --- .../templates/{ => pages}/contract.mdx.template | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/scripts/generate-docs-utils/templates/{ => pages}/contract.mdx.template (100%) diff --git a/.github/scripts/generate-docs-utils/templates/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template similarity index 100% rename from .github/scripts/generate-docs-utils/templates/contract.mdx.template rename to .github/scripts/generate-docs-utils/templates/pages/contract.mdx.template From eabc77b81e461d751a5a8e003c3a9c2e818b9d3c Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Sun, 7 Dec 2025 10:13:07 -0500 Subject: [PATCH 04/68] add mod to contract type detection --- .../doc-generation-utils.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js index 185facbd..d2f873e0 100644 --- a/.github/scripts/generate-docs-utils/doc-generation-utils.js +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -230,21 +230,23 @@ function extractModuleDescriptionFromSource(solFilePath) { */ function getContractType(filePath, content) { const lowerPath = filePath.toLowerCase(); + const normalizedPath = lowerPath.replace(/\\/g, '/'); + const baseName = path.basename(filePath, path.extname(filePath)).toLowerCase(); - // Check path patterns - files with 'lib' in the path are modules - if (lowerPath.includes('lib')) { + // Explicit modules folder + if (normalizedPath.includes('/modules/')) { + return 'module'; + } + + // File naming conventions (e.g., AccessControlMod.sol, NonReentrancyModule.sol) + if (baseName.endsWith('mod') || baseName.endsWith('module')) { return 'module'; } if (lowerPath.includes('facet')) { return 'facet'; } - - // Check content patterns - files with 'library' keyword (Solidity libraries) are modules - if (content && content.includes('library ')) { - return 'module'; - } - + // Default to facet for contracts return 'facet'; } From 53f6ebd8d6a3f7636f5ba32814bc71e13231da38 Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Sun, 7 Dec 2025 10:24:39 -0500 Subject: [PATCH 05/68] update PR content --- .../generate-docs-utils/pr-body-generator.js | 12 ++++++------ .github/workflows/generate-docs.yml | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/scripts/generate-docs-utils/pr-body-generator.js b/.github/scripts/generate-docs-utils/pr-body-generator.js index a7e8a151..54dfe2d6 100644 --- a/.github/scripts/generate-docs-utils/pr-body-generator.js +++ b/.github/scripts/generate-docs-utils/pr-body-generator.js @@ -22,7 +22,7 @@ function generatePRBody(summary) { const libraries = summary.libraries || []; const total = summary.totalGenerated || 0; - let body = '## Auto-Generated API Documentation\n\n'; + let body = '## Auto-Generated Contract Documentation\n\n'; body += 'This PR contains auto-generated documentation from contract comments using `forge doc`.\n\n'; body += '### Summary\n'; @@ -36,10 +36,10 @@ function generatePRBody(summary) { body += '\n'; } - if (libraries.length > 0) { - body += '### Libraries\n'; - libraries.forEach(lib => { - body += `- ${lib.title}\n`; + if (modules.length > 0) { + body += '### Modules\n'; + modules.forEach(module => { + body += `- ${module.title}\n`; }); body += '\n'; } @@ -57,7 +57,7 @@ function generatePRBody(summary) { body += '- [ ] Ensure consistency with existing docs\n\n'; body += '---\n'; - body += '* This PR was automatically generated. Please review before merging.*\n'; + body += 'This PR was automatically generated. Please review before merging.\n'; return body; } diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index dce0fc58..3f50dc50 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -32,7 +32,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 # Full history for git diff + fetch-depth: 0 submodules: recursive - name: Get changed Solidity files @@ -112,11 +112,11 @@ jobs: npm run build env: # Prevent build failures from missing keys - ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID || 'dummy' }} - ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY || 'dummy' }} - ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME || 'compose' }} - POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY || 'dummy' }} - continue-on-error: true # TODO: Disable when ready to merge + ALGOLIA_APP_ID: 'dummy' + ALGOLIA_API_KEY: 'dummy' + ALGOLIA_INDEX_NAME: 'dummy' + POSTHOG_API_KEY: 'dummy' + continue-on-error: false - name: Generate PR body if: steps.check-generated.outputs.has_generated == 'true' @@ -148,8 +148,8 @@ jobs: uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GITHUB_TOKEN }} - title: '[DOCS] Auto-generated API documentation' - commit-message: 'docs: auto-generate API docs from NatSpec' + title: '[DOCS] Auto-generated Contract documentation' + commit-message: 'docs: auto-generate contracts docs from NatSpec' branch: docs/auto-generated-${{ github.run_number }} body: ${{ steps.pr-body.outputs.body }} labels: | From 120348af74dbcdccde5d091d5c92f84ffed4753d Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Sun, 7 Dec 2025 10:47:54 -0500 Subject: [PATCH 06/68] fix modules var def --- .github/scripts/generate-docs-utils/pr-body-generator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/generate-docs-utils/pr-body-generator.js b/.github/scripts/generate-docs-utils/pr-body-generator.js index 54dfe2d6..2a52d66b 100644 --- a/.github/scripts/generate-docs-utils/pr-body-generator.js +++ b/.github/scripts/generate-docs-utils/pr-body-generator.js @@ -19,7 +19,7 @@ const path = require('path'); */ function generatePRBody(summary) { const facets = summary.facets || []; - const libraries = summary.libraries || []; + const modules = summary.modules || []; const total = summary.totalGenerated || 0; let body = '## Auto-Generated Contract Documentation\n\n'; @@ -57,7 +57,7 @@ function generatePRBody(summary) { body += '- [ ] Ensure consistency with existing docs\n\n'; body += '---\n'; - body += 'This PR was automatically generated. Please review before merging.\n'; + body += ' Date: Mon, 8 Dec 2025 11:57:19 -0500 Subject: [PATCH 07/68] chnage template engine toe handlebars engine --- .../templates/package-lock.json | 79 +++++++++ .../templates/package.json | 10 ++ .../templates/pages/contract.mdx.template | 38 ++--- .../templates/template-engine-handlebars.js | 157 ++++++++++++++++++ .../templates/templates.js | 10 +- .github/scripts/generate-docs.js | 2 - .github/workflows/generate-docs.yml | 5 + .gitignore | 26 +-- website/README.md | 6 + website/package.json | 3 +- website/sidebars.js | 28 ++-- 11 files changed, 302 insertions(+), 62 deletions(-) create mode 100644 .github/scripts/generate-docs-utils/templates/package-lock.json create mode 100644 .github/scripts/generate-docs-utils/templates/package.json create mode 100644 .github/scripts/generate-docs-utils/templates/template-engine-handlebars.js diff --git a/.github/scripts/generate-docs-utils/templates/package-lock.json b/.github/scripts/generate-docs-utils/templates/package-lock.json new file mode 100644 index 00000000..b1877ecc --- /dev/null +++ b/.github/scripts/generate-docs-utils/templates/package-lock.json @@ -0,0 +1,79 @@ +{ + "name": "compose-doc-templates", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "compose-doc-templates", + "version": "1.0.0", + "dependencies": { + "handlebars": "^4.7.8" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + } + } +} diff --git a/.github/scripts/generate-docs-utils/templates/package.json b/.github/scripts/generate-docs-utils/templates/package.json new file mode 100644 index 00000000..d5425ad4 --- /dev/null +++ b/.github/scripts/generate-docs-utils/templates/package.json @@ -0,0 +1,10 @@ +{ + "name": "compose-doc-templates", + "version": "1.0.0", + "private": true, + "description": "Template engine for generating MDX documentation", + "dependencies": { + "handlebars": "^4.7.8" + } +} + diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index d800952b..0c726227 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -35,6 +35,25 @@ import Callout from '@site/src/components/ui/Callout'; This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. +{{/if}} +{{#if hasStructs}} +## Structs + +{{#each structs}} +### {{name}} + +{{#if description}} +{{sanitizeMdx description}} + +{{/if}} +{{#if definition}} +```solidity +{{definition}} +``` + +{{/if}} +{{/each}} +{{/if}} {{#if hasStorage}} ## Storage @@ -55,7 +74,6 @@ This module provides internal functions for use in your custom facets. Import it {{/each}} {{/if}} {{/if}} -{{/if}} {{#if hasFunctions}} ## Functions @@ -135,24 +153,6 @@ This module provides internal functions for use in your custom facets. Import it {{signature}} ``` -{{/if}} -{{/each}} -{{/if}} -{{#if hasStructs}} -## Structs - -{{#each structs}} -### {{name}} - -{{#if description}} -{{sanitizeMdx description}} - -{{/if}} -{{#if definition}} -```solidity -{{definition}} -``` - {{/if}} {{/each}} {{/if}} diff --git a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js new file mode 100644 index 00000000..ebc92098 --- /dev/null +++ b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js @@ -0,0 +1,157 @@ +/** + * Handlebars Template Engine for MDX Documentation Generation + * + * Replaces the custom template engine with Handlebars for better reliability + * and proper MDX formatting. + */ + +const Handlebars = require('handlebars'); +const fs = require('fs'); +const path = require('path'); +const helpers = require('./helpers'); + +// Track if helpers have been registered (only register once) +let helpersRegistered = false; + +/** + * Register custom helpers for Handlebars + * All helpers from helpers.js are registered for use in templates + */ +function registerHelpers() { + if (helpersRegistered) return; + + // Register escape helpers + Handlebars.registerHelper('escapeYaml', helpers.escapeYaml); + Handlebars.registerHelper('escapeJsx', helpers.escapeJsx); + Handlebars.registerHelper('sanitizeMdx', helpers.sanitizeMdx); + Handlebars.registerHelper('escapeMarkdownTable', helpers.escapeMarkdownTable); + + // Custom helper for better null/empty string handling + // Handlebars' default #if treats empty strings as falsy, but we want to be explicit + Handlebars.registerHelper('ifTruthy', function(value, options) { + if (value != null && + !(Array.isArray(value) && value.length === 0) && + !(typeof value === 'string' && value.trim().length === 0) && + !(typeof value === 'object' && Object.keys(value).length === 0)) { + return options.fn(this); + } + return options.inverse(this); + }); + + helpersRegistered = true; +} + +/** + * Normalize MDX formatting to ensure proper blank lines + * MDX requires blank lines between: + * - Import statements and JSX + * - JSX components and markdown + * - JSX components and other JSX + * + * @param {string} content - MDX content to normalize + * @returns {string} Properly formatted MDX + */ +function normalizeMdxFormatting(content) { + if (!content) return ''; + + let normalized = content; + + // 1. Ensure blank line after import statements (before JSX) + // Pattern: import ...;\n\n## + normalized = normalized.replace(/(\/>)\n(##)/g, '$1\n\n$2'); + + // 3. Ensure blank line after JSX closing tags (before other JSX) + // Pattern: \n)\n(<[A-Z])/g, '$1\n\n$2'); + + // 4. Ensure blank line after JSX closing tags (before markdown content) + // Pattern: \n## or \n[text] + normalized = normalized.replace(/(<\/[A-Z][a-zA-Z]+>)\n(##|[A-Z])/g, '$1\n\n$2'); + + // 5. Ensure blank line before JSX components (after markdown) + // Pattern: ]\n line.trimEnd()).join('\n'); + + // 8. Ensure file ends with single newline + normalized = normalized.trimEnd() + '\n'; + + return normalized; +} + +/** + * List available template files + * @returns {string[]} Array of template names (without extension) + */ +function listAvailableTemplates() { + const templatesDir = path.join(__dirname, 'pages'); + try { + return fs.readdirSync(templatesDir) + .filter(f => f.endsWith('.mdx.template')) + .map(f => f.replace('.mdx.template', '')); + } catch (e) { + return []; + } +} + +/** + * Load and render a template file with Handlebars + * @param {string} templateName - Name of template (without extension) + * @param {object} data - Data to render + * @returns {string} Rendered template with proper MDX formatting + * @throws {Error} If template cannot be loaded + */ +function loadAndRenderTemplate(templateName, data) { + const templatePath = path.join(__dirname, 'pages', `${templateName}.mdx.template`); + + if (!fs.existsSync(templatePath)) { + const available = listAvailableTemplates(); + throw new Error( + `Template '${templateName}' not found at: ${templatePath}\n` + + `Available templates: ${available.length > 0 ? available.join(', ') : 'none'}` + ); + } + + // Register helpers (only once, but safe to call multiple times) + registerHelpers(); + + try { + // Load template + const templateContent = fs.readFileSync(templatePath, 'utf8'); + + // Compile template with Handlebars + const template = Handlebars.compile(templateContent); + + // Render with data + let rendered = template(data); + + // Post-process: normalize MDX formatting + rendered = normalizeMdxFormatting(rendered); + + return rendered; + } catch (error) { + if (error.message.includes('Parse error')) { + throw new Error( + `Template parsing error in ${templateName}: ${error.message}\n` + + `Template path: ${templatePath}` + ); + } + throw error; + } +} + +module.exports = { + loadAndRenderTemplate, + registerHelpers, + listAvailableTemplates, +}; + diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index 95b9ac16..74be407b 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -1,9 +1,9 @@ /** * MDX Templates for Docusaurus documentation - * Uses template files with a simple template engine + * Uses Handlebars template engine for reliable MDX generation */ -const { loadAndRenderTemplate } = require('./template-engine'); +const { loadAndRenderTemplate } = require('./template-engine-handlebars'); const { sanitizeForMdx } = require('./helpers'); const { readFileSafe } = require('../../workflow-utils'); @@ -398,9 +398,9 @@ function prepareBaseData(data, position = 99) { gitSource: data.gitSource || '', keyFeatures: data.keyFeatures || '', usageExample: data.usageExample || '', - bestPractices: data.bestPractices || '', - securityConsiderations: data.securityConsiderations || '', - integrationNotes: data.integrationNotes || '', + bestPractices: (data.bestPractices && data.bestPractices.trim()) ? data.bestPractices : null, + securityConsiderations: (data.securityConsiderations && data.securityConsiderations.trim()) ? data.securityConsiderations : null, + integrationNotes: (data.integrationNotes && data.integrationNotes.trim()) ? data.integrationNotes : null, storageInfo: data.storageInfo || '', // Events diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index f9129cc6..8ce02168 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -93,8 +93,6 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { enhancedData = await enhanceWithAI(data, contractType, token); } else { console.log(`Skipping AI enhancement for ${data.title}`); - // Add fallback content when skipping AI enhancement - const { addFallbackContent } = require('./generate-docs-utils/copilot-enhancement'); enhancedData = addFallbackContent(data, contractType); } diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 3f50dc50..6310973f 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -74,6 +74,11 @@ jobs: if: steps.changed-files.outputs.has_changes == 'true' run: forge doc + - name: Install template dependencies + if: steps.changed-files.outputs.has_changes == 'true' + working-directory: .github/scripts/generate-docs-utils/templates + run: npm install + - name: Run documentation generator if: steps.changed-files.outputs.has_changes == 'true' env: diff --git a/.gitignore b/.gitignore index 42f88272..e2b3ec0a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,24 +13,10 @@ out/ # Docusaurus # Dependencies -docs/node_modules - -# Production -docs/build - -# Generated files -docs/.docusaurus -docs/.cache-loader - -# Misc -docs/.DS_Store -docs/.env -docs/.env.local -docs/.env.development.local -docs/.env.test.local -docs/.env.production.local - -docs/npm-debug.log* -docs/yarn-debug.log* -docs/yarn-error.log* +website/node_modules +.github/scripts/generate-docs-utils/templates/node_modules +# Ignore forge docs output +docs/ +# Ignore Docs generation summary file +docgen-summary.json \ No newline at end of file diff --git a/website/README.md b/website/README.md index 23d9d30c..eff415d0 100644 --- a/website/README.md +++ b/website/README.md @@ -28,3 +28,9 @@ npm run build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. + +## Generate Facets & Modules Documentation + +```bash +npm run generate-docs +``` \ No newline at end of file diff --git a/website/package.json b/website/package.json index 502302dd..d5c60934 100644 --- a/website/package.json +++ b/website/package.json @@ -11,7 +11,8 @@ "clear": "docusaurus clear", "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids" + "write-heading-ids": "docusaurus write-heading-ids", + "generate-docs": "cd .. && forge doc && SKIP_ENHANCEMENT=true node .github/scripts/generate-docs.js --all" }, "dependencies": { "@docusaurus/core": "3.9.2", diff --git a/website/sidebars.js b/website/sidebars.js index d684cc43..eb0796e8 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -19,34 +19,34 @@ const sidebars = { 'intro', { type: 'category', - label: 'Foundations', - collapsed: false, - link: { - type: 'doc', - id: 'foundations/index', - }, + label: 'Getting Started', + collapsed: true, items: [ { type: 'autogenerated', - dirName: 'foundations', + dirName: 'getting-started', }, ], }, { type: 'category', - label: 'Getting Started', - collapsed: true, + label: 'Foundations', + collapsed: false, + link: { + type: 'doc', + id: 'foundations/index', + }, items: [ { type: 'autogenerated', - dirName: 'getting-started', + dirName: 'foundations', }, ], }, { type: 'category', label: 'Design', - collapsed: false, + collapsed: true, link: { type: 'doc', id: 'design/index', @@ -58,19 +58,17 @@ const sidebars = { }, ], }, - /* { type: 'category', - label: 'Facets', + label: 'Contracts', collapsed: true, items: [ { type: 'autogenerated', - dirName: 'facets', + dirName: 'contracts', }, ], }, - */ { type: 'category', label: 'Contribution', From e7d449f2f5c913099b286216946dfa41529ba51c Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Mon, 8 Dec 2025 12:14:18 -0500 Subject: [PATCH 08/68] update template --- .../templates/pages/contract.mdx.template | 61 +++++++++++++------ .../templates/templates.js | 2 + 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index 0c726227..820bf81d 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -6,6 +6,7 @@ description: "{{escapeYaml description}}" import DocHero from '@site/src/components/docs/DocHero'; import Callout from '@site/src/components/ui/Callout'; +import PropertyTable from '@site/src/components/api/PropertyTable'; {{#if isFacet}} + {{/if}} {{/if}} {{#if hasFunctions}} @@ -91,23 +97,33 @@ This module provides internal functions for use in your custom facets. Import it {{/if}} {{#if hasParams}} -**Parameters** - -| Name | Type | Description | -|------|------|-------------| + {{/if}} {{#if hasReturns}} -**Returns** - -| Name | Type | Description | -|------|------|-------------| + {{/if}} {{/each}} @@ -129,11 +145,18 @@ This module provides internal functions for use in your custom facets. Import it {{/if}} {{#if hasParams}} -| Parameter | Type | Description | -|-----------|------|-------------| + {{/if}} {{/each}} diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index 74be407b..a71a15bf 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -418,6 +418,8 @@ function prepareBaseData(data, position = 99) { // State variables (for modules) stateVariables: (data.stateVariables || []).map(v => ({ name: v.name, + type: v.type || '', + value: v.value || '', description: v.description || '', })), hasStateVariables: (data.stateVariables || []).length > 0, From bd92d896acdf08abd003a5846f267d6de9578ab5 Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Mon, 8 Dec 2025 12:27:33 -0500 Subject: [PATCH 09/68] update git ignore --- .github/workflows/generate-docs.yml | 4 ++-- .gitignore | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 6310973f..a1488896 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -145,8 +145,8 @@ jobs: # Reset any staged changes git reset - # Only stage website documentation files - git add website/docs/contracts/ + # Only stage website documentation files (force add in case they're ignored) + git add -f website/docs/contracts/ - name: Create Pull Request if: steps.check-generated.outputs.has_generated == 'true' diff --git a/.gitignore b/.gitignore index e2b3ec0a..521146a4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ out/ website/node_modules .github/scripts/generate-docs-utils/templates/node_modules -# Ignore forge docs output -docs/ +# Ignore forge docs output (root level only) +/docs/ # Ignore Docs generation summary file docgen-summary.json \ No newline at end of file From 0d2a3c5385c9a1097ab6f381d6ae5d32edf508fe Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Mon, 8 Dec 2025 12:55:22 -0500 Subject: [PATCH 10/68] fix formatting --- .github/docs-gen-prompts.md | 3 -- .../generate-docs-utils/ai-enhancement.js | 31 +++++++++++++++---- .../generate-docs-utils/templates/helpers.js | 2 ++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/.github/docs-gen-prompts.md b/.github/docs-gen-prompts.md index 1a1aa7eb..10dddf78 100644 --- a/.github/docs-gen-prompts.md +++ b/.github/docs-gen-prompts.md @@ -1,8 +1,5 @@ # AI Documentation Enhancement Prompts -This file contains all prompts and instructions used for AI-powered documentation enhancement. -Edit this file to adjust AI behavior without modifying the JavaScript code. - --- ## System Prompt diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index 16b1963f..777718a2 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -276,14 +276,33 @@ async function enhanceWithAI(data, contractType, token) { let enhanced = JSON.parse(content); console.log('✅ AI enhancement successful'); + // Convert literal \n strings to actual newlines + const convertNewlines = (str) => { + if (!str || typeof str !== 'string') return str; + return str.replace(/\\n/g, '\n'); + }; + + // Decode HTML entities (for code blocks) + const decodeHtmlEntities = (str) => { + if (!str || typeof str !== 'string') return str; + return str + .replace(/"/g, '"') + .replace(/=/g, '=') + .replace(/=>/g, '=>') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/'/g, "'") + .replace(/&/g, '&'); + }; + return { ...data, - overview: enhanced.overview || data.overview, - usageExample: enhanced.usageExample || null, - bestPractices: enhanced.bestPractices || null, - keyFeatures: enhanced.keyFeatures || null, - integrationNotes: enhanced.integrationNotes || null, - securityConsiderations: enhanced.securityConsiderations || null, + overview: convertNewlines(enhanced.overview) || data.overview, + usageExample: decodeHtmlEntities(convertNewlines(enhanced.usageExample)) || null, + bestPractices: convertNewlines(enhanced.bestPractices) || null, + keyFeatures: convertNewlines(enhanced.keyFeatures) || null, + integrationNotes: convertNewlines(enhanced.integrationNotes) || null, + securityConsiderations: convertNewlines(enhanced.securityConsiderations) || null, }; } catch (parseError) { console.log(' ⚠️ Could not parse API response as JSON'); diff --git a/.github/scripts/generate-docs-utils/templates/helpers.js b/.github/scripts/generate-docs-utils/templates/helpers.js index 6b333657..dd8a17fd 100644 --- a/.github/scripts/generate-docs-utils/templates/helpers.js +++ b/.github/scripts/generate-docs-utils/templates/helpers.js @@ -21,12 +21,14 @@ function escapeYaml(str) { /** * Sanitize text to prevent MDX from interpreting it as JSX + * Converts literal \n strings to actual newlines for proper formatting * @param {string} str - String to sanitize * @returns {string} Sanitized string */ function sanitizeForMdx(str) { if (!str) return ''; return str + .replace(/\\n/g, '\n') // Convert literal \n to actual newlines .replace(//g, '>') .replace(/\{/g, '{') From 71d9f8ed1368892b7fcc1c16f729f9c205cd8b70 Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Mon, 8 Dec 2025 18:03:21 -0500 Subject: [PATCH 11/68] improve page template --- .../templates/pages/contract.mdx.template | 212 +++++++++++------- .../templates/template-engine-handlebars.js | 92 ++++++++ .../templates/templates.js | 1 + website/docs/contracts/_category_.json | 6 + website/docs/contracts/facets/_category_.json | 6 + .../docs/contracts/modules/_category_.json | 6 + .../components/code/ExpandableCode/index.js | 32 ++- .../src/components/ui/Badge/styles.module.css | 5 + .../ui/GradientButton/styles.module.css | 30 ++- website/src/theme/EditThisPage/index.js | 37 +++ .../src/theme/EditThisPage/styles.module.css | 25 +++ 11 files changed, 351 insertions(+), 101 deletions(-) create mode 100644 website/docs/contracts/_category_.json create mode 100644 website/docs/contracts/facets/_category_.json create mode 100644 website/docs/contracts/modules/_category_.json create mode 100644 website/src/theme/EditThisPage/index.js create mode 100644 website/src/theme/EditThisPage/styles.module.css diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index 820bf81d..de4ac1c7 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -2,66 +2,77 @@ sidebar_position: {{position}} title: "{{escapeYaml title}}" description: "{{escapeYaml description}}" +{{#if gitSource}} +gitSource: "{{gitSource}}" +{{/if}} --- -import DocHero from '@site/src/components/docs/DocHero'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; -{{#if isFacet}} - -{{/if}} -## Overview + +{{escapeYaml description}} + -{{sanitizeMdx overview}} - -{{#if gitSource}} -[View Source]({{gitSource}}) - -{{/if}} {{#if keyFeatures}} {{sanitizeMdx keyFeatures}} - {{/if}} + {{#if isModule}} This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - {{/if}} -{{#if hasStructs}} -## Structs +## Overview + +{{sanitizeMdx overview}} + +--- + +## Storage + +{{#if hasStructs}} {{#each structs}} ### {{name}} {{#if description}} {{sanitizeMdx description}} - {{/if}} -{{#if definition}} -```solidity -{{definition}} -``` +{{#if definition}} + +{{{codeContent definition}}} + {{/if}} + +{{#unless @last}} +--- +{{/unless}} {{/each}} {{/if}} + {{#if hasStorage}} -## Storage {{#if storageInfo}} {{sanitizeMdx storageInfo}} - {{/if}} +--- {{#if hasStateVariables}} ### State Variables @@ -77,9 +88,9 @@ This module provides internal functions for use in your custom facets. Import it ]} showRequired={false} /> - {{/if}} {{/if}} + {{#if hasFunctions}} ## Functions @@ -88,15 +99,17 @@ This module provides internal functions for use in your custom facets. Import it {{#if description}} {{sanitizeMdx description}} - {{/if}} -{{#if signature}} -```solidity -{{signature}} -``` +{{#if signature}} + +{{{codeContent signature}}} + {{/if}} + {{#if hasParams}} +**Parameters:** + - {{/if}} + {{#if hasReturns}} +**Returns:** + - {{/if}} + +{{#unless @last}} +--- +{{/unless}} {{/each}} {{/if}} + {{#if hasEvents}} ## Events + {{#each events}} -### {{name}} - -{{#if description}} -{{sanitizeMdx description}} - -{{/if}} -{{#if signature}} -```solidity -{{signature}} -``` - -{{/if}} -{{#if hasParams}} - + {{#if description}} +
+ {{sanitizeMdx description}} +
+ {{/if}} + + {{#if signature}} +
+ Signature: + +{{{codeContent signature}}} + +
+ {{/if}} + + {{#if hasParams}} +
+ Parameters: + - -{{/if}} + ]} + showRequired={false} + /> +
+ {{/if}} + {{/each}} +
{{/if}} + {{#if hasErrors}} ## Errors + {{#each errors}} -### {{name}} - -{{#if description}} -{{sanitizeMdx description}} - -{{/if}} -{{#if signature}} -```solidity + + {{#if description}} +
+ {{sanitizeMdx description}} +
+ {{/if}} + + {{#if signature}} +
+ Signature: + {{signature}} -``` - -{{/if}} + +
+ {{/if}} +
{{/each}} +
{{/if}} + {{#if usageExample}} ## Usage Example -```solidity -{{usageExample}} -``` - + +{{{codeContent usageExample}}} + {{/if}} + {{#if bestPractices}} ## Best Practices {{sanitizeMdx bestPractices}} - {{/if}} + {{#if isFacet}} {{#if securityConsiderations}} ## Security Considerations @@ -202,9 +238,9 @@ This module provides internal functions for use in your custom facets. Import it {{sanitizeMdx securityConsiderations}} - {{/if}} {{/if}} + {{#if isModule}} {{#if integrationNotes}} ## Integration Notes @@ -212,7 +248,27 @@ This module provides internal functions for use in your custom facets. Import it {{sanitizeMdx integrationNotes}} - {{/if}} {{/if}} +
+ +
+ + + +{{#if relatedDocs}} + +{{/if}} diff --git a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js index ebc92098..bb123693 100644 --- a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js +++ b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js @@ -26,6 +26,98 @@ function registerHelpers() { Handlebars.registerHelper('sanitizeMdx', helpers.sanitizeMdx); Handlebars.registerHelper('escapeMarkdownTable', helpers.escapeMarkdownTable); + // Helper to emit a JSX style literal: returns a string like {{display: "flex", gap: "1rem"}} + Handlebars.registerHelper('styleLiteral', function(styles) { + if (!styles || typeof styles !== 'string') return '{{}}'; + + const styleObj = {}; + const pairs = styles.split(';').filter(pair => pair.trim()); + + pairs.forEach(pair => { + const [key, value] = pair.split(':').map(s => s.trim()); + if (key && value !== undefined) { + const camelKey = key.includes('-') + ? key.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + : key; + const cleanValue = value.replace(/^["']|["']$/g, ''); + styleObj[camelKey] = cleanValue; + } + }); + + const entries = Object.entries(styleObj).map(([k, v]) => { + const isPureNumber = /^-?\d+\.?\d*$/.test(v.trim()); + // Quote everything except pure numbers + const valueLiteral = isPureNumber ? v : JSON.stringify(v); + return `${k}: ${valueLiteral}`; + }).join(', '); + + // Wrap with double braces so MDX sees style={{...}} + return `{{${entries}}}`; + }); + + // Helper to wrap code content in template literal for MDX + // This ensures MDX treats the content as a string, not JSX to parse + Handlebars.registerHelper('codeContent', function(content) { + if (!content) return '{``}'; + // Escape backticks in the content + const escaped = String(content).replace(/`/g, '\\`').replace(/\$/g, '\\$'); + return `{\`${escaped}\`}`; + }); + + // Helper to generate JSX style object syntax + // Accepts CSS string and converts to JSX object format + // Handles both kebab-case (margin-bottom) and camelCase (marginBottom) + Handlebars.registerHelper('jsxStyle', function(styles) { + if (!styles || typeof styles !== 'string') return '{}'; + + // Parse CSS string like "display: flex; margin-bottom: 1rem;" or "marginBottom: 1rem" + const styleObj = {}; + const pairs = styles.split(';').filter(pair => pair.trim()); + + pairs.forEach(pair => { + const [key, value] = pair.split(':').map(s => s.trim()); + if (key && value) { + // Convert kebab-case to camelCase if needed + const camelKey = key.includes('-') + ? key.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) + : key; + // Remove quotes from value if present + const cleanValue = value.replace(/^["']|["']$/g, ''); + styleObj[camelKey] = cleanValue; + } + }); + + // Convert to JSX object string with proper quoting + // All CSS values should be quoted as strings unless they're pure numbers + const entries = Object.entries(styleObj) + .map(([k, v]) => { + // Check if it's a pure number (integer or decimal without units) + if (/^-?\d+\.?\d*$/.test(v.trim())) { + return `${k}: ${v}`; + } + // Everything else (including CSS units like "0.75rem", "2rem", CSS vars, etc.) should be quoted + return `${k}: ${JSON.stringify(v)}`; + }) + .join(', '); + + // Return the object content wrapped in braces + // When used with {{{jsxStyle ...}}} in template, this becomes style={...} + // But we need style={{...}}, so we return with an extra opening brace + // The template uses {{{jsxStyle ...}}} which outputs raw, giving us style={{{...}}} + // To get style={{...}}, we need the helper to return {{...}} + // But with triple braces in template, we'd get style={{{{...}}}} which is wrong + // Solution: return just the object, template adds one brace manually + // Return the full JSX object expression with double braces + // Template will use raw block: {{{{jsxStyle ...}}}} + // This outputs: style={{{display: "flex", ...}}} + // But we need: style={{display: "flex", ...}} + // Actually, let's try: helper returns {{...}}, template uses {{jsxStyle}} (double) + // Handlebars will output the helper result + // But it will escape... unless we use raw block + // Simplest: return {{...}}, use {{{{jsxStyle}}}} raw block + return `{{${entries}}}`; + }); + // Custom helper for better null/empty string handling // Handlebars' default #if treats empty strings as falsy, but we want to be explicit Handlebars.registerHelper('ifTruthy', function(value, options) { diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index a71a15bf..fc45172f 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -395,6 +395,7 @@ function prepareBaseData(data, position = 99) { description, subtitle, overview, + generatedDate: data.generatedDate || new Date().toISOString(), gitSource: data.gitSource || '', keyFeatures: data.keyFeatures || '', usageExample: data.usageExample || '', diff --git a/website/docs/contracts/_category_.json b/website/docs/contracts/_category_.json new file mode 100644 index 00000000..57623619 --- /dev/null +++ b/website/docs/contracts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Contracts", + "position": 4, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/website/docs/contracts/facets/_category_.json b/website/docs/contracts/facets/_category_.json new file mode 100644 index 00000000..83c31a59 --- /dev/null +++ b/website/docs/contracts/facets/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Facets", + "position": 1, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/website/docs/contracts/modules/_category_.json b/website/docs/contracts/modules/_category_.json new file mode 100644 index 00000000..134b5727 --- /dev/null +++ b/website/docs/contracts/modules/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules", + "position": 2, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/website/src/components/code/ExpandableCode/index.js b/website/src/components/code/ExpandableCode/index.js index fa868a9b..30602b05 100644 --- a/website/src/components/code/ExpandableCode/index.js +++ b/website/src/components/code/ExpandableCode/index.js @@ -1,4 +1,5 @@ -import React, { useState } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; +import CodeBlock from '@theme/CodeBlock'; import Icon from '../../ui/Icon'; import clsx from 'clsx'; import styles from './styles.module.css'; @@ -18,34 +19,29 @@ export default function ExpandableCode({ children }) { const [isExpanded, setIsExpanded] = useState(false); - const codeRef = React.useRef(null); - const [needsExpansion, setNeedsExpansion] = React.useState(false); - - React.useEffect(() => { - if (codeRef.current) { - const lines = codeRef.current.textContent.split('\n').length; - setNeedsExpansion(lines > maxLines); - } - }, [children, maxLines]); + const [needsExpansion, setNeedsExpansion] = useState(false); const codeContent = typeof children === 'string' ? children : children?.props?.children || ''; + const lineCount = useMemo(() => codeContent.split('\n').length, [codeContent]); + + useEffect(() => { + setNeedsExpansion(lineCount > maxLines); + }, [lineCount, maxLines]); return (
{title &&
{title}
}
-
-          {codeContent}
-        
+ {codeContent} + {needsExpansion && ( diff --git a/website/src/components/ui/Badge/styles.module.css b/website/src/components/ui/Badge/styles.module.css index eee3e93d..855d0556 100644 --- a/website/src/components/ui/Badge/styles.module.css +++ b/website/src/components/ui/Badge/styles.module.css @@ -8,6 +8,11 @@ transition: all 0.2s ease; } +/* Prevent Markdown-wrapped children from adding extra space */ +.badge p { + margin: 0; +} + /* Sizes */ .badge.small { padding: 0.25rem 0.625rem; diff --git a/website/src/components/ui/GradientButton/styles.module.css b/website/src/components/ui/GradientButton/styles.module.css index 5bead8be..9aeb3753 100644 --- a/website/src/components/ui/GradientButton/styles.module.css +++ b/website/src/components/ui/GradientButton/styles.module.css @@ -1,8 +1,12 @@ .gradientButton { position: relative; + padding-left: 0px; display: inline-flex; align-items: center; justify-content: center; + box-sizing: border-box; + line-height: 1; + vertical-align: middle; font-weight: 600; text-decoration: none; border: none; @@ -14,10 +18,21 @@ } .buttonContent { + display: inline-flex; + align-items: center; + gap: 0.35rem; + line-height: 1; position: relative; z-index: 2; } +/* Prevent Markdown-wrapped children from adding extra space */ +.gradientButton p, +.buttonContent p { + margin: 0; + color: white; +} + .buttonGlow { position: absolute; top: 50%; @@ -46,18 +61,18 @@ /* Sizes */ .gradientButton.small { - padding: 0.5rem 1rem; - font-size: 0.875rem; + padding: 0.55rem 1rem; + font-size: 0.9rem; } .gradientButton.medium { - padding: 0.65rem 1.25rem; + padding: 0.7rem 1.3rem; font-size: 1rem; } .gradientButton.large { - padding: 0.875rem 1.75rem; - font-size: 1.125rem; + padding: 0.9rem 1.75rem; + font-size: 1.05rem; } /* Variants */ @@ -66,6 +81,11 @@ color: white; } +.gradientButton:visited, +.gradientButton * { + color: inherit; +} + .gradientButton.secondary { background: linear-gradient(135deg, #60a5fa 0%, #2563eb 100%); color: white; diff --git a/website/src/theme/EditThisPage/index.js b/website/src/theme/EditThisPage/index.js new file mode 100644 index 00000000..d9da8659 --- /dev/null +++ b/website/src/theme/EditThisPage/index.js @@ -0,0 +1,37 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import {useDoc} from '@docusaurus/plugin-content-docs/client'; +import styles from './styles.module.css'; + +export default function EditThisPage({editUrl}) { + const {frontMatter} = useDoc(); + const viewSource = frontMatter?.gitSource; + + // Nothing to show + if (!editUrl && !viewSource) { + return null; + } + + return ( +
+ {viewSource && ( + + + View Source + + )} + {editUrl && ( + + + Edit this page + + )} +
+ ); +} + diff --git a/website/src/theme/EditThisPage/styles.module.css b/website/src/theme/EditThisPage/styles.module.css new file mode 100644 index 00000000..e705316b --- /dev/null +++ b/website/src/theme/EditThisPage/styles.module.css @@ -0,0 +1,25 @@ +.wrapper { + display: inline-flex; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} + +.link { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-weight: 600; + color: var(--ifm-link-color); + text-decoration: none; +} + +.link:hover { + text-decoration: underline; + color: var(--ifm-link-hover-color, var(--ifm-link-color)); +} + +.link:visited { + color: var(--ifm-link-color); +} + From 91e3fa5a2273e55700ae3c84c4e8f908b1f19777 Mon Sep 17 00:00:00 2001 From: Maxime Normandin Date: Tue, 9 Dec 2025 09:29:16 -0500 Subject: [PATCH 12/68] improve prompt --- .github/docs-gen-prompts.md | 55 ++++++++++--------- website/src/theme/EditThisPage/index.js | 1 + .../src/theme/EditThisPage/styles.module.css | 1 + 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/.github/docs-gen-prompts.md b/.github/docs-gen-prompts.md index 10dddf78..5b057b16 100644 --- a/.github/docs-gen-prompts.md +++ b/.github/docs-gen-prompts.md @@ -4,16 +4,29 @@ ## System Prompt -You are a Solidity smart contract documentation expert for the Compose framework. -Always respond with valid JSON only, no markdown formatting. -Follow the project conventions and style guidelines strictly. +You are the Compose Solidity documentation orchestrator. Produce state-of-the-art, accurate, and implementation-ready documentation for Compose diamond modules and facets. Always respond with valid JSON only (no markdown). Follow all appended guideline sections from `copilot-instructions.md`, Compose conventions, and the templates below. + +- Audience: Solidity engineers building on Compose diamonds. Prioritize clarity, precision, and developer actionability. +- Grounding: Use only the provided contract data. Do not invent functions, storage layouts, events, errors, modules, or behaviors. Keep terminology aligned with Compose (diamond proxy, facets, modules, storage pattern). +- Tone and style: Active voice, concise sentences, zero fluff/marketing. Prefer imperative guidance over vague descriptions. +- Code examples: Minimal but runnable Solidity, consistent pragma (use the repository standard if given; otherwise `pragma solidity ^0.8.30;`). Import and call the actual functions exactly as named. Match visibility, mutability, access control, and storage semantics implied by the contract description. +- Output contract details only through the specified JSON fields. Do not add extra keys or reorder fields. Escape newlines as `\\n` inside JSON strings. + +### Quality Guardrails (must stay in the system prompt) + +- Hallucinations: no invented APIs, behaviors, dependencies, or storage details beyond the supplied context. +- Vagueness and filler: avoid generic statements like “this is very useful”; be specific to the module/facet and diamond pattern. +- Repetition and redundancy: do not restate inputs verbatim or repeat the same idea in multiple sections. +- Passive, wordy, or hedging language: prefer direct, active phrasing without needless qualifiers. +- Inaccurate code: wrong function names/params/visibility, missing imports, or examples that can’t compile. +- Inconsistency: maintain a steady tense, voice, and terminology; keep examples consistent with the described functions. +- Overclaiming: no security, performance, or compatibility claims that are not explicitly supported by the context. --- ## Relevant Guideline Sections -These section headers from copilot-instructions.md will be extracted and appended to the system prompt. -One section per line. Must match exactly as they appear in copilot-instructions.md. +These section headers from `copilot-instructions.md` are appended to the system prompt to enforce Compose-wide standards. One section per line; must match exactly. ``` ## 3. Core Philosophy @@ -27,17 +40,13 @@ One section per line. Must match exactly as they appear in copilot-instructions. ## Module Prompt Template -Given this module documentation from the Compose diamond proxy framework, enhance it by generating: - -1. **overview**: A clear, concise overview (2-3 sentences) explaining what this module does and why it's useful in the context of diamond contracts. - -2. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this module. Show importing and calling functions. - -3. **bestPractices**: 2-3 bullet points of best practices for using this module. +Given this module documentation from the Compose diamond proxy framework, enhance it by generating developer-grade content that is specific, actionable, and faithful to the provided contract data. -4. **integrationNotes**: A note about how this module works with diamond storage pattern and how changes made through it are visible to facets. - -5. **keyFeatures**: A brief bullet list of key features. +1. **overview**: 2-3 sentence overview of what the module does and why it matters for diamonds (storage reuse, composition, safety). +2. **usageExample**: 10-20 lines of Solidity demonstrating how a facet would import and call this module. Use the real function names and signatures; include pragma and any required imports. Keep it minimal but compilable. +3. **bestPractices**: 2-3 bullets focused on safe and idiomatic use (access control, storage hygiene, upgrade awareness, error handling). +4. **integrationNotes**: Explain how the module interacts with diamond storage and how changes are visible to facets; note any invariants or ordering requirements. +5. **keyFeatures**: 2-4 bullets highlighting unique capabilities, constraints, or guarantees. Contract Information: - Name: {{title}} @@ -59,17 +68,13 @@ Respond ONLY with valid JSON in this exact format (no markdown code blocks, no e ## Facet Prompt Template -Given this facet documentation from the Compose diamond proxy framework, enhance it by generating: - -1. **overview**: A clear, concise overview (2-3 sentences) explaining what this facet does and why it's useful in the context of diamond contracts. - -2. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this facet. Show how it would be used in a diamond. - -3. **bestPractices**: 2-3 bullet points of best practices for using this facet. - -4. **securityConsiderations**: Important security considerations when using this facet (access control, reentrancy, etc.). +Given this facet documentation from the Compose diamond proxy framework, enhance it by generating precise, implementation-ready guidance. -5. **keyFeatures**: A brief bullet list of key features. +1. **overview**: 2-3 sentence summary of the facet’s purpose and value inside a diamond (routing, orchestration, surface area). +2. **usageExample**: 10-20 lines showing how this facet is deployed or invoked within a diamond. Include pragma, imports, selector usage, and sample calls that reflect the real function names and signatures. +3. **bestPractices**: 2-3 bullets on correct integration patterns (initialization, access control, storage handling, upgrade safety). +4. **securityConsiderations**: Concise notes on access control, reentrancy, input validation, and any state-coupling risks specific to this facet. +5. **keyFeatures**: 2-4 bullets calling out unique abilities, constraints, or guarantees. Contract Information: - Name: {{title}} diff --git a/website/src/theme/EditThisPage/index.js b/website/src/theme/EditThisPage/index.js index d9da8659..781ff979 100644 --- a/website/src/theme/EditThisPage/index.js +++ b/website/src/theme/EditThisPage/index.js @@ -35,3 +35,4 @@ export default function EditThisPage({editUrl}) { ); } + diff --git a/website/src/theme/EditThisPage/styles.module.css b/website/src/theme/EditThisPage/styles.module.css index e705316b..fc7a21e0 100644 --- a/website/src/theme/EditThisPage/styles.module.css +++ b/website/src/theme/EditThisPage/styles.module.css @@ -23,3 +23,4 @@ color: var(--ifm-link-color); } + From dc1eea85f1b0cd24b0167119cc2493eb7d5828e5 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 10:27:07 -0500 Subject: [PATCH 13/68] fix edit links rendering --- .../theme/EditThisPage/DocsEditThisPage.js | 47 +++++++++++++++ .../EditThisPage/SafeDocsEditThisPage.js | 35 +++++++++++ .../theme/EditThisPage/SimpleEditThisPage.js | 25 ++++++++ website/src/theme/EditThisPage/index.js | 58 +++++++++---------- 4 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 website/src/theme/EditThisPage/DocsEditThisPage.js create mode 100644 website/src/theme/EditThisPage/SafeDocsEditThisPage.js create mode 100644 website/src/theme/EditThisPage/SimpleEditThisPage.js diff --git a/website/src/theme/EditThisPage/DocsEditThisPage.js b/website/src/theme/EditThisPage/DocsEditThisPage.js new file mode 100644 index 00000000..e0697a14 --- /dev/null +++ b/website/src/theme/EditThisPage/DocsEditThisPage.js @@ -0,0 +1,47 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import {useDoc} from '@docusaurus/plugin-content-docs/client'; +import styles from './styles.module.css'; + +/** + * DocsEditThisPage component for documentation pages + * Uses useDoc hook to access frontMatter for "View Source" link + * + * WARNING: This component should ONLY be rendered when we're certain + * we're in a docs page context. It will throw an error if used outside + * the DocProvider context. + * + * @param {string} editUrl - URL to edit the page + */ +export default function DocsEditThisPage({editUrl}) { + const {frontMatter} = useDoc(); + const viewSource = frontMatter?.gitSource; + + // Nothing to show + if (!editUrl && !viewSource) { + return null; + } + + return ( +
+ {viewSource && ( + + + View Source + + )} + {editUrl && ( + + + Edit this page + + )} +
+ ); +} + diff --git a/website/src/theme/EditThisPage/SafeDocsEditThisPage.js b/website/src/theme/EditThisPage/SafeDocsEditThisPage.js new file mode 100644 index 00000000..7da71c5d --- /dev/null +++ b/website/src/theme/EditThisPage/SafeDocsEditThisPage.js @@ -0,0 +1,35 @@ +import React from 'react'; +import DocsEditThisPage from './DocsEditThisPage'; +import SimpleEditThisPage from './SimpleEditThisPage'; + +/** + * Error boundary wrapper for DocsEditThisPage + * Catches errors if useDoc hook is called outside DocProvider context + * Falls back to SimpleEditThisPage if an error occurs + * + * @param {string} editUrl - URL to edit the page + */ +export default class SafeDocsEditThisPage extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error) { + // If useDoc fails, fall back to simple version + return { hasError: true }; + } + + componentDidCatch(error, errorInfo) { + // Error caught, will render fallback + // Could log to error reporting service here if needed + } + + render() { + if (this.state.hasError) { + return ; + } + + return ; + } +} diff --git a/website/src/theme/EditThisPage/SimpleEditThisPage.js b/website/src/theme/EditThisPage/SimpleEditThisPage.js new file mode 100644 index 00000000..ad5a8553 --- /dev/null +++ b/website/src/theme/EditThisPage/SimpleEditThisPage.js @@ -0,0 +1,25 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import styles from './styles.module.css'; + +/** + * Simple EditThisPage component for non-docs contexts (blog, etc.) + * Safe to use anywhere - doesn't require any special context + * + * @param {string} editUrl - URL to edit the page + */ +export default function SimpleEditThisPage({editUrl}) { + if (!editUrl) { + return null; + } + + return ( +
+ + + Edit this page + +
+ ); +} + diff --git a/website/src/theme/EditThisPage/index.js b/website/src/theme/EditThisPage/index.js index 781ff979..db62da16 100644 --- a/website/src/theme/EditThisPage/index.js +++ b/website/src/theme/EditThisPage/index.js @@ -1,38 +1,34 @@ import React from 'react'; -import Link from '@docusaurus/Link'; -import {useDoc} from '@docusaurus/plugin-content-docs/client'; -import styles from './styles.module.css'; +import {useLocation} from '@docusaurus/router'; +import SimpleEditThisPage from './SimpleEditThisPage'; +import SafeDocsEditThisPage from './SafeDocsEditThisPage'; +/** + * Main EditThisPage component + * + * Intelligently renders the appropriate EditThisPage variant based on the current route: + * - Docs pages (/docs/*): Uses SafeDocsEditThisPage (with useDoc hook, wrapped in error boundary) + * - Other pages: Uses SimpleEditThisPage + * + * Route checking is necessary because error boundaries don't work reliably during SSR/build. + * + * @param {string} editUrl - URL to edit the page + */ export default function EditThisPage({editUrl}) { - const {frontMatter} = useDoc(); - const viewSource = frontMatter?.gitSource; + let isDocsPage = false; + + try { + const location = useLocation(); + const pathname = location?.pathname || ''; + + isDocsPage = pathname.startsWith('/docs/'); + } catch (error) { + isDocsPage = false; + } - // Nothing to show - if (!editUrl && !viewSource) { - return null; + if (isDocsPage) { + return ; } - return ( -
- {viewSource && ( - - - View Source - - )} - {editUrl && ( - - - Edit this page - - )} -
- ); + return ; } - - From c96b0cfc6f3d9e9ed44be16f0013d67627238281 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 10:34:44 -0500 Subject: [PATCH 14/68] add new trigger input to specified files --- .github/workflows/generate-docs.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index a1488896..6a5ddfb8 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -7,6 +7,10 @@ on: - 'src/**/*.sol' workflow_dispatch: inputs: + target_file: + description: 'Process ONLY the specified Solidity file(s) (relative path, e.g. src/contracts/MyFacet.sol or src/facets/A.sol,src/facets/B.sol)' + required: false + type: string process_all: description: 'Process ALL Solidity files' required: false @@ -38,7 +42,22 @@ jobs: - name: Get changed Solidity files id: changed-files run: | - if [ "${{ github.event.inputs.process_all }}" == "true" ]; then + # Prefer explicit target_file when provided via manual dispatch. + # You can pass a single file or a comma/space-separated list, e.g.: + # src/facets/A.sol,src/facets/B.sol + # src/facets/A.sol src/facets/B.sol + if [ -n "${{ github.event.inputs.target_file }}" ]; then + echo "Processing Solidity file(s) from input:" + echo "${{ github.event.inputs.target_file }}" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "process_all=false" >> $GITHUB_OUTPUT + # Normalize comma/space-separated list into one file path per line + echo "${{ github.event.inputs.target_file }}" \ + | tr ',' '\n' \ + | tr ' ' '\n' \ + | sed '/^$/d' \ + > /tmp/changed_sol_files.txt + elif [ "${{ github.event.inputs.process_all }}" == "true" ]; then echo "Processing all Solidity files (manual trigger)" echo "has_changes=true" >> $GITHUB_OUTPUT echo "process_all=true" >> $GITHUB_OUTPUT From 117a9e586e335bd5662e5aba533c315582d9ef9b Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 15:51:36 -0500 Subject: [PATCH 15/68] improve rate limiting --- .../generate-docs-utils/ai-enhancement.js | 124 ++++++++++++------ 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index 777718a2..a61eff79 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -13,6 +13,8 @@ const { sleep, makeHttpsRequest } = require('../workflow-utils'); // We add 7 seconds between calls to stay safe (60/10 = 6, +1 buffer) const RATE_LIMIT_DELAY_MS = 8000; let lastApiCallTime = 0; +// Maximum number of times to retry a single request after hitting a 429 +const MAX_RATE_LIMIT_RETRIES = 3; const AI_PROMPT_PATH = path.join(__dirname, '../../docs-gen-prompts.md'); const REPO_INSTRUCTIONS_PATH = path.join(__dirname, '../../copilot-instructions.md'); @@ -267,54 +269,90 @@ async function enhanceWithAI(data, contractType, token) { try { await waitForRateLimit(); - const response = await makeHttpsRequest(options, requestBody); - if (response.choices && response.choices[0] && response.choices[0].message) { - const content = response.choices[0].message.content; - - try { - let enhanced = JSON.parse(content); - console.log('✅ AI enhancement successful'); - - // Convert literal \n strings to actual newlines - const convertNewlines = (str) => { - if (!str || typeof str !== 'string') return str; - return str.replace(/\\n/g, '\n'); - }; - - // Decode HTML entities (for code blocks) - const decodeHtmlEntities = (str) => { - if (!str || typeof str !== 'string') return str; - return str - .replace(/"/g, '"') - .replace(/=/g, '=') - .replace(/=>/g, '=>') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/'/g, "'") - .replace(/&/g, '&'); - }; + // Helper to handle a single API call and common response parsing + const performApiCall = async () => { + const response = await makeHttpsRequest(options, requestBody); + + if (response.choices && response.choices[0] && response.choices[0].message) { + const content = response.choices[0].message.content; - return { - ...data, - overview: convertNewlines(enhanced.overview) || data.overview, - usageExample: decodeHtmlEntities(convertNewlines(enhanced.usageExample)) || null, - bestPractices: convertNewlines(enhanced.bestPractices) || null, - keyFeatures: convertNewlines(enhanced.keyFeatures) || null, - integrationNotes: convertNewlines(enhanced.integrationNotes) || null, - securityConsiderations: convertNewlines(enhanced.securityConsiderations) || null, - }; - } catch (parseError) { - console.log(' ⚠️ Could not parse API response as JSON'); - console.log(' Response:', content.substring(0, 200)); + try { + let enhanced = JSON.parse(content); + console.log('✅ AI enhancement successful'); + + // Convert literal \n strings to actual newlines + const convertNewlines = (str) => { + if (!str || typeof str !== 'string') return str; + return str.replace(/\\n/g, '\n'); + }; + + // Decode HTML entities (for code blocks) + const decodeHtmlEntities = (str) => { + if (!str || typeof str !== 'string') return str; + return str + .replace(/"/g, '"') + .replace(/=/g, '=') + .replace(/=>/g, '=>') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/'/g, "'") + .replace(/&/g, '&'); + }; + + return { + ...data, + overview: convertNewlines(enhanced.overview) || data.overview, + usageExample: decodeHtmlEntities(convertNewlines(enhanced.usageExample)) || null, + bestPractices: convertNewlines(enhanced.bestPractices) || null, + keyFeatures: convertNewlines(enhanced.keyFeatures) || null, + integrationNotes: convertNewlines(enhanced.integrationNotes) || null, + securityConsiderations: convertNewlines(enhanced.securityConsiderations) || null, + }; + } catch (parseError) { + console.log(' ⚠️ Could not parse API response as JSON'); + console.log(' Response:', content.substring(0, 200)); + return addFallbackContent(data, contractType); + } + } + + console.log(' ⚠️ Unexpected API response format'); + return addFallbackContent(data, contractType); + }; + + let attempt = 0; + // Retry loop for handling minute-token 429s while still eventually + // progressing through all files in the run. + // We respect the guidance from the API by waiting ~60s before retries. + // eslint-disable-next-line no-constant-condition + while (true) { + try { + return await performApiCall(); + } catch (error) { + const msg = error && error.message ? error.message : ''; + + // Detect GitHub Models minute-token rate limit (HTTP 429) + if (msg.startsWith('HTTP 429:') && attempt < MAX_RATE_LIMIT_RETRIES) { + attempt += 1; + console.log( + ` ⚠️ GitHub Models rate limit reached (minute tokens). ` + + `Waiting 60s before retry ${attempt}/${MAX_RATE_LIMIT_RETRIES}...` + ); + await sleep(60000); + continue; + } + + if (msg.startsWith('HTTP 429:')) { + console.log(' ⚠️ Rate limit persisted after maximum retries, using fallback content'); + } else { + console.log(` ⚠️ GitHub Models API error: ${msg}`); + } + return addFallbackContent(data, contractType); } } - - console.log(' ⚠️ Unexpected API response format'); - return addFallbackContent(data, contractType); - } catch (error) { - console.log(` ⚠️ GitHub Models API error: ${error.message}`); + } catch (outerError) { + console.log(` ⚠️ GitHub Models API error (outer): ${outerError.message}`); return addFallbackContent(data, contractType); } } From 326d9e4b8d8f7ef437a77f59f91bb1619c2d1a6c Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 16:13:22 -0500 Subject: [PATCH 16/68] add better log for debug --- .../generate-docs-utils/ai-enhancement.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index a61eff79..f6f275af 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -277,6 +277,15 @@ async function enhanceWithAI(data, contractType, token) { if (response.choices && response.choices[0] && response.choices[0].message) { const content = response.choices[0].message.content; + // Debug: Log full response content + console.log(' 📋 Full API response content:'); + console.log(' ' + '='.repeat(80)); + console.log(content); + console.log(' ' + '='.repeat(80)); + console.log(' Response length:', content.length, 'chars'); + console.log(' First 100 chars:', JSON.stringify(content.substring(0, 100))); + console.log(' Last 100 chars:', JSON.stringify(content.substring(Math.max(0, content.length - 100)))); + try { let enhanced = JSON.parse(content); console.log('✅ AI enhancement successful'); @@ -311,12 +320,20 @@ async function enhanceWithAI(data, contractType, token) { }; } catch (parseError) { console.log(' ⚠️ Could not parse API response as JSON'); - console.log(' Response:', content.substring(0, 200)); + console.log(' Parse error:', parseError.message); + console.log(' Error stack:', parseError.stack); + console.log(' Response type:', typeof content); + console.log(' Response length:', content ? content.length : 'null/undefined'); + if (content) { + console.log(' First 500 chars:', JSON.stringify(content.substring(0, 500))); + console.log(' Last 500 chars:', JSON.stringify(content.substring(Math.max(0, content.length - 500)))); + } return addFallbackContent(data, contractType); } } console.log(' ⚠️ Unexpected API response format'); + console.log(' Response structure:', JSON.stringify(response, null, 2).substring(0, 1000)); return addFallbackContent(data, contractType); }; From c7bcb226e7a4199b05b623565af1e9b353748fea Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 19:21:03 -0500 Subject: [PATCH 17/68] add token rate limiter --- .../generate-docs-utils/ai-enhancement.js | 235 +++++++++++++----- .github/scripts/generate-docs-utils/config.js | 5 +- .../generate-docs-utils/token-rate-limiter.js | 216 ++++++++++++++++ 3 files changed, 396 insertions(+), 60 deletions(-) create mode 100644 .github/scripts/generate-docs-utils/token-rate-limiter.js diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index f6f275af..547c29b9 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -8,34 +8,19 @@ const fs = require('fs'); const path = require('path'); const { models: MODELS_CONFIG } = require('./config'); const { sleep, makeHttpsRequest } = require('../workflow-utils'); +const { + estimateTokenUsage, + waitForRateLimit, + recordTokenConsumption, + updateLastTokenConsumption, +} = require('./token-rate-limiter'); -// Rate limiting: GitHub Models has 10 requests per 60s limit -// We add 7 seconds between calls to stay safe (60/10 = 6, +1 buffer) -const RATE_LIMIT_DELAY_MS = 8000; -let lastApiCallTime = 0; // Maximum number of times to retry a single request after hitting a 429 const MAX_RATE_LIMIT_RETRIES = 3; const AI_PROMPT_PATH = path.join(__dirname, '../../docs-gen-prompts.md'); const REPO_INSTRUCTIONS_PATH = path.join(__dirname, '../../copilot-instructions.md'); -/** - * Wait for rate limit if needed - * Ensures at least RATE_LIMIT_DELAY_MS between API calls - */ -async function waitForRateLimit() { - const now = Date.now(); - const elapsed = now - lastApiCallTime; - - if (lastApiCallTime > 0 && elapsed < RATE_LIMIT_DELAY_MS) { - const waitTime = RATE_LIMIT_DELAY_MS - elapsed; - console.log(` ⏳ Rate limit: waiting ${Math.ceil(waitTime / 1000)}s...`); - await sleep(waitTime); - } - - lastApiCallTime = Date.now(); -} - // Load repository instructions for context let REPO_INSTRUCTIONS = ''; try { @@ -221,6 +206,112 @@ Respond ONLY with valid JSON in this exact format (no markdown code blocks, no e }`; } +/** + * Convert enhanced data fields (newlines, HTML entities) + * @param {object} enhanced - Parsed JSON from API + * @param {object} data - Original documentation data + * @returns {object} Enhanced data with converted fields + */ +function convertEnhancedFields(enhanced, data) { + // Convert literal \n strings to actual newlines + const convertNewlines = (str) => { + if (!str || typeof str !== 'string') return str; + return str.replace(/\\n/g, '\n'); + }; + + // Decode HTML entities (for code blocks) + const decodeHtmlEntities = (str) => { + if (!str || typeof str !== 'string') return str; + return str + .replace(/"/g, '"') + .replace(/=/g, '=') + .replace(/=>/g, '=>') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/'/g, "'") + .replace(/&/g, '&'); + }; + + return { + ...data, + overview: convertNewlines(enhanced.overview) || data.overview, + usageExample: decodeHtmlEntities(convertNewlines(enhanced.usageExample)) || null, + bestPractices: convertNewlines(enhanced.bestPractices) || null, + keyFeatures: convertNewlines(enhanced.keyFeatures) || null, + integrationNotes: convertNewlines(enhanced.integrationNotes) || null, + securityConsiderations: convertNewlines(enhanced.securityConsiderations) || null, + }; +} + +/** + * Extract and clean JSON from API response + * Handles markdown code blocks, wrapped text, and attempts to fix truncated JSON + * @param {string} content - Raw API response content + * @returns {string} Cleaned JSON string ready for parsing + */ +function extractJSON(content) { + if (!content || typeof content !== 'string') { + return content; + } + + let cleaned = content.trim(); + + // Remove markdown code blocks (```json ... ``` or ``` ... ```) + // Handle both at start and anywhere in the string + cleaned = cleaned.replace(/^```(?:json)?\s*\n?/gm, ''); + cleaned = cleaned.replace(/\n?```\s*$/gm, ''); + cleaned = cleaned.trim(); + + // Find the first { and last } to extract JSON object + const firstBrace = cleaned.indexOf('{'); + const lastBrace = cleaned.lastIndexOf('}'); + + if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) { + cleaned = cleaned.substring(firstBrace, lastBrace + 1); + } else if (firstBrace !== -1) { + // We have a { but no closing }, JSON might be truncated + cleaned = cleaned.substring(firstBrace); + } + + // Try to fix common truncation issues + const openBraces = (cleaned.match(/\{/g) || []).length; + const closeBraces = (cleaned.match(/\}/g) || []).length; + + if (openBraces > closeBraces) { + // JSON might be truncated - try to close incomplete strings and objects + // Check if we're in the middle of a string (simple heuristic) + const lastChar = cleaned[cleaned.length - 1]; + const lastQuote = cleaned.lastIndexOf('"'); + const lastBraceInCleaned = cleaned.lastIndexOf('}'); + + // If last quote is after last brace and not escaped, we might be in a string + if (lastQuote > lastBraceInCleaned && lastChar !== '"') { + // Check if the quote before last is escaped + let isEscaped = false; + for (let i = lastQuote - 1; i >= 0 && cleaned[i] === '\\'; i--) { + isEscaped = !isEscaped; + } + + if (!isEscaped) { + // We're likely in an incomplete string, close it + cleaned = cleaned + '"'; + } + } + + // Close any incomplete objects/arrays + const missingBraces = openBraces - closeBraces; + // Try to intelligently close - if we're in the middle of a property, add a value first + const trimmed = cleaned.trim(); + if (trimmed.endsWith(',') || trimmed.endsWith(':')) { + // We're in the middle of a property, add null and close + cleaned = cleaned.replace(/[,:]\s*$/, ': null'); + } + cleaned = cleaned + '\n' + '}'.repeat(missingBraces); + } + + return cleaned.trim(); +} + /** * Enhance documentation data using GitHub Copilot * @param {object} data - Parsed documentation data @@ -236,6 +327,10 @@ async function enhanceWithAI(data, contractType, token) { const systemPrompt = buildSystemPrompt(); const userPrompt = buildPrompt(data, contractType); + const maxTokens = MODELS_CONFIG.maxTokens; + + // Estimate token usage for this request (for rate limiting) + const estimatedTokens = estimateTokenUsage(systemPrompt, userPrompt, maxTokens); const requestBody = JSON.stringify({ messages: [ @@ -249,7 +344,7 @@ async function enhanceWithAI(data, contractType, token) { }, ], model: MODELS_CONFIG.model, - max_tokens: MODELS_CONFIG.maxTokens, + max_tokens: maxTokens, }); // GitHub Models uses Azure AI inference endpoint @@ -268,12 +363,24 @@ async function enhanceWithAI(data, contractType, token) { }; try { - await waitForRateLimit(); + // Wait for both time-based and token-based rate limits + await waitForRateLimit(estimatedTokens); // Helper to handle a single API call and common response parsing const performApiCall = async () => { const response = await makeHttpsRequest(options, requestBody); + // Record token consumption (use estimated, or actual if available in response) + if (response.usage && response.usage.total_tokens) { + // GitHub Models API provides actual usage - use it for more accurate tracking + const actualTokens = response.usage.total_tokens; + recordTokenConsumption(actualTokens); + console.log(` 📊 Actual token usage: ${actualTokens} tokens`); + } else { + // Fallback to estimated tokens if API doesn't provide usage + recordTokenConsumption(estimatedTokens); + } + if (response.choices && response.choices[0] && response.choices[0].message) { const content = response.choices[0].message.content; @@ -287,47 +394,57 @@ async function enhanceWithAI(data, contractType, token) { console.log(' Last 100 chars:', JSON.stringify(content.substring(Math.max(0, content.length - 100)))); try { - let enhanced = JSON.parse(content); - console.log('✅ AI enhancement successful'); - - // Convert literal \n strings to actual newlines - const convertNewlines = (str) => { - if (!str || typeof str !== 'string') return str; - return str.replace(/\\n/g, '\n'); - }; - - // Decode HTML entities (for code blocks) - const decodeHtmlEntities = (str) => { - if (!str || typeof str !== 'string') return str; - return str - .replace(/"/g, '"') - .replace(/=/g, '=') - .replace(/=>/g, '=>') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/'/g, "'") - .replace(/&/g, '&'); - }; + // First, try to parse directly (most responses should be valid JSON) + let enhanced; + try { + enhanced = JSON.parse(content); + console.log('✅ AI enhancement successful (direct parse)'); + } catch (directParseError) { + // If direct parse fails, try to extract and clean JSON + console.log(' Direct parse failed, attempting to extract JSON...'); + const cleanedContent = extractJSON(content); + console.log(' Cleaned content length:', cleanedContent.length, 'chars'); + + enhanced = JSON.parse(cleanedContent); + console.log('✅ AI enhancement successful (after extraction)'); + } - return { - ...data, - overview: convertNewlines(enhanced.overview) || data.overview, - usageExample: decodeHtmlEntities(convertNewlines(enhanced.usageExample)) || null, - bestPractices: convertNewlines(enhanced.bestPractices) || null, - keyFeatures: convertNewlines(enhanced.keyFeatures) || null, - integrationNotes: convertNewlines(enhanced.integrationNotes) || null, - securityConsiderations: convertNewlines(enhanced.securityConsiderations) || null, - }; + return convertEnhancedFields(enhanced, data); } catch (parseError) { console.log(' ⚠️ Could not parse API response as JSON'); console.log(' Parse error:', parseError.message); - console.log(' Error stack:', parseError.stack); - console.log(' Response type:', typeof content); - console.log(' Response length:', content ? content.length : 'null/undefined'); - if (content) { - console.log(' First 500 chars:', JSON.stringify(content.substring(0, 500))); - console.log(' Last 500 chars:', JSON.stringify(content.substring(Math.max(0, content.length - 500)))); + + // As a last resort, try one more time with extraction + try { + const cleanedContent = extractJSON(content); + console.log(' Attempting final parse with extracted JSON...'); + const enhanced = JSON.parse(cleanedContent); + console.log('✅ AI enhancement successful (final attempt with extraction)'); + + return convertEnhancedFields(enhanced, data); + } catch (finalError) { + console.log(' Final parse attempt also failed:', finalError.message); + console.log(' Error position:', finalError.message.match(/position (\d+)/)?.[1] || 'unknown'); + + // Show debugging info + try { + const cleanedContent = extractJSON(content); + console.log(' Cleaned content (first 500 chars):', JSON.stringify(cleanedContent.substring(0, 500))); + console.log(' Cleaned content (last 500 chars):', JSON.stringify(cleanedContent.substring(Math.max(0, cleanedContent.length - 500)))); + + // Try to find the error position in cleaned content + const errorPosMatch = finalError.message.match(/position (\d+)/); + if (errorPosMatch) { + const errorPos = parseInt(errorPosMatch[1], 10); + const start = Math.max(0, errorPos - 50); + const end = Math.min(cleanedContent.length, errorPos + 50); + console.log(' Error context (chars ' + start + '-' + end + '):', JSON.stringify(cleanedContent.substring(start, end))); + } + } catch (e) { + console.log(' Could not extract/clean content:', e.message); + } } + return addFallbackContent(data, contractType); } } diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index 6b71e32e..48c0b402 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -22,7 +22,10 @@ module.exports = { models: { host: 'models.inference.ai.azure.com', model: 'gpt-4o', - maxTokens: 2000, + // Increased for better quality documentation with more detailed content + // Token-aware rate limiting (token-rate-limiter.js) ensures we stay within + // the 40k tokens/minute limit while allowing higher per-request token usage + maxTokens: 3500, }, }; diff --git a/.github/scripts/generate-docs-utils/token-rate-limiter.js b/.github/scripts/generate-docs-utils/token-rate-limiter.js new file mode 100644 index 00000000..702c6953 --- /dev/null +++ b/.github/scripts/generate-docs-utils/token-rate-limiter.js @@ -0,0 +1,216 @@ +/** + * Token-Aware Rate Limiter for GitHub Models API + * + * Handles both time-based and token-based rate limiting to stay within: + * - 10 requests per 60 seconds (request rate limit) + * - 40,000 tokens per 60 seconds (minute-token limit) + * + */ + +const { sleep } = require('../workflow-utils'); + +// GitHub Models API Limits +const MAX_REQUESTS_PER_MINUTE = 10; +const MAX_TOKENS_PER_MINUTE = 40000; + +// Safety margins to avoid hitting limits +const REQUEST_SAFETY_BUFFER_MS = 1000; // Add 1s buffer to request spacing +const TOKEN_BUDGET_SAFETY_MARGIN = 0.85; // Use 85% of token budget + +// Calculated values +const REQUEST_DELAY_MS = Math.ceil((60000 / MAX_REQUESTS_PER_MINUTE) + REQUEST_SAFETY_BUFFER_MS); +const EFFECTIVE_TOKEN_BUDGET = MAX_TOKENS_PER_MINUTE * TOKEN_BUDGET_SAFETY_MARGIN; +const TOKEN_WINDOW_MS = 60000; // 60 second rolling window + +// State tracking +let lastApiCallTime = 0; +let tokenConsumptionHistory = []; // Array of { timestamp, tokens } + +/** + * Estimate token usage for a request + * Uses a rough heuristic: ~4 characters per token for input text + * @param {string} systemPrompt - System prompt text + * @param {string} userPrompt - User prompt text + * @param {number} maxTokens - Max tokens requested for completion + * @returns {number} Estimated total tokens (input + output) + */ +function estimateTokenUsage(systemPrompt, userPrompt, maxTokens) { + const inputText = (systemPrompt || '') + (userPrompt || ''); + // Rough estimate: ~4 characters per token for GPT-4 models + const estimatedInputTokens = Math.ceil(inputText.length / 4); + // Add max_tokens for potential output (worst case: we use all requested tokens) + return estimatedInputTokens + maxTokens; +} + +/** + * Clean expired entries from token consumption history + * Removes entries older than TOKEN_WINDOW_MS + */ +function cleanTokenHistory() { + const now = Date.now(); + tokenConsumptionHistory = tokenConsumptionHistory.filter( + entry => (now - entry.timestamp) < TOKEN_WINDOW_MS + ); +} + +/** + * Get current token consumption in the rolling window + * @returns {number} Total tokens consumed in the last 60 seconds + */ +function getCurrentTokenConsumption() { + cleanTokenHistory(); + return tokenConsumptionHistory.reduce((sum, entry) => sum + entry.tokens, 0); +} + +/** + * Record token consumption for rate limiting + * @param {number} tokens - Tokens consumed in the request + */ +function recordTokenConsumption(tokens) { + tokenConsumptionHistory.push({ + timestamp: Date.now(), + tokens: tokens, + }); + cleanTokenHistory(); +} + +/** + * Update the last recorded token consumption with actual usage from API response + * @param {number} actualTokens - Actual tokens used (from API response) + */ +function updateLastTokenConsumption(actualTokens) { + if (tokenConsumptionHistory.length > 0) { + const lastEntry = tokenConsumptionHistory[tokenConsumptionHistory.length - 1]; + lastEntry.tokens = actualTokens; + } +} + +/** + * Calculate wait time needed for token budget to free up + * @param {number} tokensNeeded - Tokens needed for the next request + * @param {number} currentConsumption - Current token consumption + * @returns {number} Milliseconds to wait (0 if no wait needed) + */ +function calculateTokenWaitTime(tokensNeeded, currentConsumption) { + const availableTokens = EFFECTIVE_TOKEN_BUDGET - currentConsumption; + + if (tokensNeeded <= availableTokens) { + return 0; // No wait needed + } + + // Need to wait for some tokens to expire from the rolling window + if (tokenConsumptionHistory.length === 0) { + return 0; // No history, shouldn't happen but handle gracefully + } + + // Find how many tokens need to expire + const tokensToFree = tokensNeeded - availableTokens; + let freedTokens = 0; + let oldestTimestamp = Date.now(); + + // Walk through history from oldest to newest + for (const entry of tokenConsumptionHistory) { + freedTokens += entry.tokens; + oldestTimestamp = entry.timestamp; + + if (freedTokens >= tokensToFree) { + break; + } + } + + // Calculate wait time until that entry expires + const now = Date.now(); + const timeUntilExpiry = TOKEN_WINDOW_MS - (now - oldestTimestamp); + + // Add small buffer to ensure the tokens have actually expired + return Math.max(0, timeUntilExpiry + 1000); +} + +/** + * Wait for rate limits if needed (both time-based and token-based) + * This is the main entry point for rate limiting before making an API call + * + * @param {number} estimatedTokens - Estimated tokens for the upcoming request + * @returns {Promise} + */ +async function waitForRateLimit(estimatedTokens) { + const now = Date.now(); + + // 1. Check time-based rate limit (requests per minute) + const elapsed = now - lastApiCallTime; + if (lastApiCallTime > 0 && elapsed < REQUEST_DELAY_MS) { + const waitTime = REQUEST_DELAY_MS - elapsed; + console.log(` ⏳ Rate limit: waiting ${Math.ceil(waitTime / 1000)}s (request spacing)...`); + await sleep(waitTime); + } + + // 2. Check token-based rate limit + cleanTokenHistory(); + const currentConsumption = getCurrentTokenConsumption(); + const availableTokens = EFFECTIVE_TOKEN_BUDGET - currentConsumption; + + if (estimatedTokens > availableTokens) { + const waitTime = calculateTokenWaitTime(estimatedTokens, currentConsumption); + + if (waitTime > 0) { + console.log( + ` ⏳ Token budget: ${currentConsumption.toFixed(0)}/${EFFECTIVE_TOKEN_BUDGET.toFixed(0)} tokens used. ` + + `Need ${estimatedTokens} tokens. Waiting ${Math.ceil(waitTime / 1000)}s for budget to reset...` + ); + await sleep(waitTime); + cleanTokenHistory(); // Re-clean after waiting + } + } else { + const remainingTokens = availableTokens - estimatedTokens; + console.log( + ` 📊 Token budget: ${currentConsumption.toFixed(0)}/${EFFECTIVE_TOKEN_BUDGET.toFixed(0)} used, ` + + `~${estimatedTokens} needed, ~${remainingTokens.toFixed(0)} remaining after this request` + ); + } + + // Update last call time + lastApiCallTime = Date.now(); +} + +/** + * Get current rate limiter statistics (useful for debugging/monitoring) + * @returns {object} Statistics object + */ +function getStats() { + cleanTokenHistory(); + const currentConsumption = getCurrentTokenConsumption(); + + return { + requestDelayMs: REQUEST_DELAY_MS, + maxTokensPerMinute: MAX_TOKENS_PER_MINUTE, + effectiveTokenBudget: EFFECTIVE_TOKEN_BUDGET, + currentTokenConsumption: currentConsumption, + availableTokens: EFFECTIVE_TOKEN_BUDGET - currentConsumption, + tokenHistoryEntries: tokenConsumptionHistory.length, + lastApiCallTime: lastApiCallTime, + timeSinceLastCall: lastApiCallTime > 0 ? Date.now() - lastApiCallTime : null, + }; +} + +/** + * Reset rate limiter state (useful for testing) + */ +function reset() { + lastApiCallTime = 0; + tokenConsumptionHistory = []; +} + +module.exports = { + estimateTokenUsage, + waitForRateLimit, + recordTokenConsumption, + updateLastTokenConsumption, + getCurrentTokenConsumption, + getStats, + reset, + // Export constants for testing/configuration + MAX_REQUESTS_PER_MINUTE, + MAX_TOKENS_PER_MINUTE, + EFFECTIVE_TOKEN_BUDGET, +}; + From 56196d950dad5f803c271d96961275928a79b519 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 19:40:38 -0500 Subject: [PATCH 18/68] improve rate limiter --- .../generate-docs-utils/ai-enhancement.js | 40 +++-- .github/scripts/generate-docs-utils/config.js | 7 +- .../generate-docs-utils/token-rate-limiter.js | 138 +++++++++++++++++- 3 files changed, 165 insertions(+), 20 deletions(-) diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index 547c29b9..6fedb26a 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -13,6 +13,7 @@ const { waitForRateLimit, recordTokenConsumption, updateLastTokenConsumption, + calculate429WaitTime, } = require('./token-rate-limiter'); // Maximum number of times to retry a single request after hitting a 429 @@ -370,18 +371,21 @@ async function enhanceWithAI(data, contractType, token) { const performApiCall = async () => { const response = await makeHttpsRequest(options, requestBody); - // Record token consumption (use estimated, or actual if available in response) - if (response.usage && response.usage.total_tokens) { - // GitHub Models API provides actual usage - use it for more accurate tracking - const actualTokens = response.usage.total_tokens; - recordTokenConsumption(actualTokens); - console.log(` 📊 Actual token usage: ${actualTokens} tokens`); - } else { - // Fallback to estimated tokens if API doesn't provide usage - recordTokenConsumption(estimatedTokens); - } - + // Only record token consumption if we got a successful response + // (not a 429 or other error that would throw before reaching here) if (response.choices && response.choices[0] && response.choices[0].message) { + // Record token consumption (use actual if available, otherwise estimated) + if (response.usage && response.usage.total_tokens) { + // GitHub Models API provides actual usage - use it for more accurate tracking + const actualTokens = response.usage.total_tokens; + recordTokenConsumption(actualTokens); + console.log(` 📊 Actual token usage: ${actualTokens} tokens`); + } else { + // Fallback to estimated tokens if API doesn't provide usage + recordTokenConsumption(estimatedTokens); + console.log(` 📊 Estimated token usage: ${estimatedTokens} tokens`); + } + const content = response.choices[0].message.content; // Debug: Log full response content @@ -457,7 +461,7 @@ async function enhanceWithAI(data, contractType, token) { let attempt = 0; // Retry loop for handling minute-token 429s while still eventually // progressing through all files in the run. - // We respect the guidance from the API by waiting ~60s before retries. + // We calculate the exact wait time based on when tokens will be available. // eslint-disable-next-line no-constant-condition while (true) { try { @@ -468,11 +472,19 @@ async function enhanceWithAI(data, contractType, token) { // Detect GitHub Models minute-token rate limit (HTTP 429) if (msg.startsWith('HTTP 429:') && attempt < MAX_RATE_LIMIT_RETRIES) { attempt += 1; + + // Calculate smart wait time based on token budget + const waitTime = calculate429WaitTime(estimatedTokens); + const waitSeconds = Math.ceil(waitTime / 1000); + console.log( ` ⚠️ GitHub Models rate limit reached (minute tokens). ` + - `Waiting 60s before retry ${attempt}/${MAX_RATE_LIMIT_RETRIES}...` + `Waiting ${waitSeconds}s for token budget to reset (retry ${attempt}/${MAX_RATE_LIMIT_RETRIES})...` ); - await sleep(60000); + await sleep(waitTime); + + // Re-check rate limit before retrying + await waitForRateLimit(estimatedTokens); continue; } diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index 48c0b402..f314a131 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -22,10 +22,11 @@ module.exports = { models: { host: 'models.inference.ai.azure.com', model: 'gpt-4o', - // Increased for better quality documentation with more detailed content + // Balanced setting for quality documentation while respecting rate limits // Token-aware rate limiting (token-rate-limiter.js) ensures we stay within - // the 40k tokens/minute limit while allowing higher per-request token usage - maxTokens: 3500, + // the 40k tokens/minute limit. Setting to 2500 allows ~10-12 requests/minute + // with typical prompt sizes, providing good quality without hitting limits. + maxTokens: 2500, }, }; diff --git a/.github/scripts/generate-docs-utils/token-rate-limiter.js b/.github/scripts/generate-docs-utils/token-rate-limiter.js index 702c6953..19318e89 100644 --- a/.github/scripts/generate-docs-utils/token-rate-limiter.js +++ b/.github/scripts/generate-docs-utils/token-rate-limiter.js @@ -4,28 +4,133 @@ * Handles both time-based and token-based rate limiting to stay within: * - 10 requests per 60 seconds (request rate limit) * - 40,000 tokens per 60 seconds (minute-token limit) + * - Daily limits (requests and tokens per day) * */ +const fs = require('fs'); +const path = require('path'); const { sleep } = require('../workflow-utils'); // GitHub Models API Limits const MAX_REQUESTS_PER_MINUTE = 10; const MAX_TOKENS_PER_MINUTE = 40000; +// Daily limits (conservative estimates - adjust based on your actual tier) +// Free tier typically has lower limits, paid tiers have higher +const MAX_REQUESTS_PER_DAY = 1500; // Conservative estimate +const MAX_TOKENS_PER_DAY = 150000; // Conservative estimate + // Safety margins to avoid hitting limits const REQUEST_SAFETY_BUFFER_MS = 1000; // Add 1s buffer to request spacing -const TOKEN_BUDGET_SAFETY_MARGIN = 0.85; // Use 85% of token budget +const TOKEN_BUDGET_SAFETY_MARGIN = 0.85; // Use 85% of minute budget +const DAILY_BUDGET_SAFETY_MARGIN = 0.90; // Use 90% of daily budget // Calculated values const REQUEST_DELAY_MS = Math.ceil((60000 / MAX_REQUESTS_PER_MINUTE) + REQUEST_SAFETY_BUFFER_MS); const EFFECTIVE_TOKEN_BUDGET = MAX_TOKENS_PER_MINUTE * TOKEN_BUDGET_SAFETY_MARGIN; +const EFFECTIVE_DAILY_REQUESTS = Math.floor(MAX_REQUESTS_PER_DAY * DAILY_BUDGET_SAFETY_MARGIN); +const EFFECTIVE_DAILY_TOKENS = Math.floor(MAX_TOKENS_PER_DAY * DAILY_BUDGET_SAFETY_MARGIN); const TOKEN_WINDOW_MS = 60000; // 60 second rolling window -// State tracking +// State tracking (minute-level) let lastApiCallTime = 0; let tokenConsumptionHistory = []; // Array of { timestamp, tokens } +// Daily tracking +const DAILY_USAGE_FILE = path.join(__dirname, '.daily-usage.json'); +let dailyUsage = loadDailyUsage(); + +/** + * Load daily usage from file + * @returns {object} Daily usage data + */ +function loadDailyUsage() { + try { + if (fs.existsSync(DAILY_USAGE_FILE)) { + const data = JSON.parse(fs.readFileSync(DAILY_USAGE_FILE, 'utf8')); + const today = new Date().toISOString().split('T')[0]; + + // Reset if it's a new day + if (data.date !== today) { + return { date: today, requests: 0, tokens: 0 }; + } + + return data; + } + } catch (error) { + console.warn('Could not load daily usage file:', error.message); + } + + // Default: new day + return { + date: new Date().toISOString().split('T')[0], + requests: 0, + tokens: 0, + }; +} + +/** + * Save daily usage to file + */ +function saveDailyUsage() { + try { + fs.writeFileSync(DAILY_USAGE_FILE, JSON.stringify(dailyUsage, null, 2)); + } catch (error) { + console.warn('Could not save daily usage file:', error.message); + } +} + +/** + * Check if daily limits would be exceeded + * @param {number} estimatedTokens - Tokens needed for next request + * @returns {object} { exceeded: boolean, reason: string } + */ +function checkDailyLimits(estimatedTokens) { + const today = new Date().toISOString().split('T')[0]; + + // Reset if new day + if (dailyUsage.date !== today) { + dailyUsage = { date: today, requests: 0, tokens: 0 }; + saveDailyUsage(); + } + + // Check request limit + if (dailyUsage.requests >= EFFECTIVE_DAILY_REQUESTS) { + return { + exceeded: true, + reason: `Daily request limit reached (${dailyUsage.requests}/${EFFECTIVE_DAILY_REQUESTS})`, + }; + } + + // Check token limit + if (dailyUsage.tokens + estimatedTokens > EFFECTIVE_DAILY_TOKENS) { + return { + exceeded: true, + reason: `Daily token limit would be exceeded (${dailyUsage.tokens + estimatedTokens}/${EFFECTIVE_DAILY_TOKENS})`, + }; + } + + return { exceeded: false }; +} + +/** + * Record daily usage + * @param {number} tokens - Tokens consumed + */ +function recordDailyUsage(tokens) { + const today = new Date().toISOString().split('T')[0]; + + // Reset if new day + if (dailyUsage.date !== today) { + dailyUsage = { date: today, requests: 0, tokens: 0 }; + } + + dailyUsage.requests += 1; + dailyUsage.tokens += tokens; + saveDailyUsage(); +} + /** * Estimate token usage for a request * Uses a rough heuristic: ~4 characters per token for input text @@ -123,7 +228,33 @@ function calculateTokenWaitTime(tokensNeeded, currentConsumption) { const timeUntilExpiry = TOKEN_WINDOW_MS - (now - oldestTimestamp); // Add small buffer to ensure the tokens have actually expired - return Math.max(0, timeUntilExpiry + 1000); + return Math.max(0, timeUntilExpiry + 2000); +} + +/** + * Calculate wait time for 429 rate limit recovery + * When we hit a 429, we need to wait for enough tokens to free up from the window + * @param {number} tokensNeeded - Tokens needed for the next request + * @returns {number} Milliseconds to wait + */ +function calculate429WaitTime(tokensNeeded) { + cleanTokenHistory(); + const currentConsumption = getCurrentTokenConsumption(); + const availableTokens = EFFECTIVE_TOKEN_BUDGET - currentConsumption; + + if (tokensNeeded <= availableTokens) { + // We should have budget, but got 429 anyway - wait for oldest entry to expire + if (tokenConsumptionHistory.length > 0) { + const oldestEntry = tokenConsumptionHistory[0]; + const now = Date.now(); + const timeUntilExpiry = TOKEN_WINDOW_MS - (now - oldestEntry.timestamp); + return Math.max(5000, timeUntilExpiry + 2000); // At least 5s, plus buffer + } + return 10000; // Default 10s if no history + } + + // Calculate how long until we have enough budget + return calculateTokenWaitTime(tokensNeeded, currentConsumption); } /** @@ -208,6 +339,7 @@ module.exports = { getCurrentTokenConsumption, getStats, reset, + calculate429WaitTime, // Export constants for testing/configuration MAX_REQUESTS_PER_MINUTE, MAX_TOKENS_PER_MINUTE, From 215af4418f36624bca16f7574d5379c5ba75a278 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 15 Dec 2025 20:57:29 -0500 Subject: [PATCH 19/68] try fix ai output --- .github/scripts/generate-docs-utils/ai-enhancement.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index 6fedb26a..0c7761b8 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -247,6 +247,7 @@ function convertEnhancedFields(enhanced, data) { /** * Extract and clean JSON from API response * Handles markdown code blocks, wrapped text, and attempts to fix truncated JSON + * Also removes control characters that break JSON parsing * @param {string} content - Raw API response content * @returns {string} Cleaned JSON string ready for parsing */ @@ -263,6 +264,10 @@ function extractJSON(content) { cleaned = cleaned.replace(/\n?```\s*$/gm, ''); cleaned = cleaned.trim(); + // Remove control characters (0x00-0x1F except newline, tab, carriage return) + // These are illegal in JSON strings and cause "Bad control character" parsing errors + cleaned = cleaned.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, ''); + // Find the first { and last } to extract JSON object const firstBrace = cleaned.indexOf('{'); const lastBrace = cleaned.lastIndexOf('}'); From bbed36d463260a77f2268b62da11a649ad6688d2 Mon Sep 17 00:00:00 2001 From: MN Date: Tue, 16 Dec 2025 19:30:22 -0500 Subject: [PATCH 20/68] add modular ai providers --- .github/scripts/ai-provider/README.md | 179 +++++++++ .github/scripts/ai-provider/index.js | 134 +++++++ .../scripts/ai-provider/provider-factory.js | 65 ++++ .../ai-provider/providers/base-provider.js | 64 ++++ .../scripts/ai-provider/providers/gemini.js | 94 +++++ .../ai-provider/providers/github-models.js | 82 +++++ .github/scripts/ai-provider/rate-limiter.js | 147 ++++++++ .../generate-docs-utils/ai-enhancement.js | 225 ++--------- .github/scripts/generate-docs-utils/config.js | 16 +- .../generate-docs-utils/token-rate-limiter.js | 348 ------------------ .github/workflows/generate-docs.yml | 2 + 11 files changed, 801 insertions(+), 555 deletions(-) create mode 100644 .github/scripts/ai-provider/README.md create mode 100644 .github/scripts/ai-provider/index.js create mode 100644 .github/scripts/ai-provider/provider-factory.js create mode 100644 .github/scripts/ai-provider/providers/base-provider.js create mode 100644 .github/scripts/ai-provider/providers/gemini.js create mode 100644 .github/scripts/ai-provider/providers/github-models.js create mode 100644 .github/scripts/ai-provider/rate-limiter.js delete mode 100644 .github/scripts/generate-docs-utils/token-rate-limiter.js diff --git a/.github/scripts/ai-provider/README.md b/.github/scripts/ai-provider/README.md new file mode 100644 index 00000000..58ad2218 --- /dev/null +++ b/.github/scripts/ai-provider/README.md @@ -0,0 +1,179 @@ +# AI Provider Service + +Simple, configurable AI service for CI workflows supporting multiple providers. + +## Features + +- **Simple API**: One function to call any AI model +- **Multiple Providers**: GitHub Models (GPT-4o) and Google Gemini +- **Auto-detection**: Automatically uses available provider +- **Rate Limiting**: Built-in request and token-based rate limiting +- **Configurable**: Override provider and model via environment variables + +## Supported Providers + +| Provider | Models | Rate Limits | API Key | +|----------|--------|-------------|---------| +| **GitHub Models** | gpt-4o, gpt-4o-mini | 10 req/min, 40k tokens/min | `GITHUB_TOKEN` | +| **Google Gemini** | gemini-1.5-flash, gemini-1.5-pro | 15 req/min, 1M tokens/min | `GOOGLE_AI_API_KEY` | + +## Usage + +### Basic Usage + +```javascript +const ai = require('./ai-provider'); + +const response = await ai.call( + 'You are a helpful assistant', // system prompt + 'Explain quantum computing' // user prompt +); + +console.log(response); +``` + +### With Options + +```javascript +const response = await ai.call( + systemPrompt, + userPrompt, + { + maxTokens: 1000, + onSuccess: (text, tokens) => { + console.log(`Success! Used ${tokens} tokens`); + }, + onError: (error) => { + console.error('Failed:', error); + } + } +); +``` + +## Environment Variables + +### Provider Selection + +```bash +# Auto-detect (default) - Try other provider with fallback to Github +AI_PROVIDER=auto + +# Use specific provider +AI_PROVIDER=github # Use GitHub Models +AI_PROVIDER=gemini # Use Google Gemini +``` + +### Model Override + +```bash +# Override default model for the provider +AI_MODEL=gpt-4o # For GitHub Models +AI_MODEL=gemini-1.5-pro # For Gemini +``` + +### API Keys + +```bash +# Google Gemini +GOOGLE_AI_API_KEY= +``` + +## Examples + +## GitHub Actions Integration + +```yaml +- name: Run AI-powered task + env: + # Option 1: Auto-detect (recommended) + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }} + + # Option 2: Force specific provider + # AI_PROVIDER: 'gemini' + # AI_MODEL: 'gemini-1.5-pro' + run: node .github/scripts/your-script.js +``` + +## Architecture + +``` +ai-provider/ +├── index.js # Main service (singleton) +├── base-provider.js # Base provider class +├── provider-factory.js # Provider creation logic +├── rate-limiter.js # Rate limiting logic +└── providers/ + ├── github-models.js # GitHub Models implementation + └── gemini.js # Gemini implementation +``` + +## Adding a New Provider + +1. Create a new provider class in `providers/`: + +```javascript +const BaseAIProvider = require('../base-provider'); + +class MyProvider extends BaseAIProvider { + constructor(config, apiKey) { + super('My Provider', config, apiKey); + } + + buildRequestOptions() { + // Return HTTP request options + } + + buildRequestBody(systemPrompt, userPrompt, maxTokens) { + // Return JSON.stringify(...) of request body + } + + extractContent(response) { + // Return { content: string, tokens: number|null } + } +} + +module.exports = MyProvider; +``` + +2. Register in `provider-factory.js`: + +```javascript +const MyProvider = require('./providers/my-provider'); + +function createMyProvider(customModel) { + const apiKey = process.env.MY_PROVIDER_API_KEY; + if (!apiKey) return null; + + return new MyProvider({ model: customModel || 'default-model' }, apiKey); +} +``` + +3. Add to auto-detection or switch statement. + +## Rate Limiting + +The service automatically handles rate limiting: + +- **Request-based**: Ensures minimum delay between requests +- **Token-based**: Tracks token consumption in a 60-second rolling window +- **Smart waiting**: Calculates exact wait time needed + +Rate limits are provider-specific and configured automatically. + +## Error Handling + +```javascript +try { + const response = await ai.call(systemPrompt, userPrompt); + // Use response +} catch (error) { + if (error.message.includes('429')) { + console.log('Rate limited - try again later'); + } else if (error.message.includes('401')) { + console.log('Invalid API key'); + } else { + console.log('Other error:', error.message); + } +} +``` diff --git a/.github/scripts/ai-provider/index.js b/.github/scripts/ai-provider/index.js new file mode 100644 index 00000000..a2f97bd5 --- /dev/null +++ b/.github/scripts/ai-provider/index.js @@ -0,0 +1,134 @@ +/** + * AI Provider Service + * Simple, configurable AI service supporting multiple providers + * + * Usage: + * const ai = require('./ai-provider'); + * const response = await ai.call(systemPrompt, userPrompt); + * + * Environment Variables: + * AI_PROVIDER - 'github' | 'gemini' | 'auto' (default: auto) + * AI_MODEL - Override default model + * GITHUB_TOKEN - For GitHub Models + * GOOGLE_AI_API_KEY - For Gemini + */ + +const { getProvider } = require('./provider-factory'); +const RateLimiter = require('./rate-limiter'); + +class AIProvider { + constructor() { + this.provider = null; + this.rateLimiter = new RateLimiter(); + this.initialized = false; + } + + /** + * Initialize the provider (lazy loading) + */ + _init() { + if (this.initialized) { + return; + } + + this.provider = getProvider(); + if (!this.provider) { + throw new Error( + 'No AI provider available. Set AI_PROVIDER or corresponding API key.' + ); + } + + this.rateLimiter.setProvider(this.provider); + this.initialized = true; + } + + /** + * Make an AI call + * + * @param {string} systemPrompt - System prompt + * @param {string} userPrompt - User prompt + * @param {object} options - Optional settings + * @param {number} options.maxTokens - Override max tokens + * @param {function} options.onSuccess - Success callback + * @param {function} options.onError - Error callback + * @returns {Promise} Response text + */ + async call(systemPrompt, userPrompt, options = {}) { + this._init(); + + const { + maxTokens = null, + onSuccess = null, + onError = null, + } = options; + + if (!systemPrompt || !userPrompt) { + throw new Error('systemPrompt and userPrompt are required'); + } + + try { + // Estimate tokens and wait for rate limits + const tokensToUse = maxTokens || this.provider.getMaxTokens(); + const estimatedTokens = this.rateLimiter.estimateTokenUsage( + systemPrompt, + userPrompt, + tokensToUse + ); + + await this.rateLimiter.waitForRateLimit(estimatedTokens); + + // Build and send request + const requestBody = this.provider.buildRequestBody(systemPrompt, userPrompt, tokensToUse); + const requestOptions = this.provider.buildRequestOptions(); + + const response = await this._makeRequest(requestOptions, requestBody); + + // Extract content + const extracted = this.provider.extractContent(response); + if (!extracted) { + throw new Error('Invalid response format from API'); + } + + // Record actual token usage + const actualTokens = extracted.tokens || estimatedTokens; + this.rateLimiter.recordTokenConsumption(actualTokens); + + if (onSuccess) { + onSuccess(extracted.content, actualTokens); + } + + return extracted.content; + + } catch (error) { + console.error(` ❌ AI call failed: ${error.message}`); + + if (onError) { + onError(error); + } + + throw error; + } + } + + /** + * Make HTTPS request + */ + async _makeRequest(options, body) { + const { makeHttpsRequest } = require('../workflow-utils'); + return await makeHttpsRequest(options, body); + } + + /** + * Get provider info + */ + getProviderInfo() { + this._init(); + return { + name: this.provider.name, + limits: this.provider.getRateLimits(), + maxTokens: this.provider.getMaxTokens(), + }; + } +} + +module.exports = new AIProvider(); \ No newline at end of file diff --git a/.github/scripts/ai-provider/provider-factory.js b/.github/scripts/ai-provider/provider-factory.js new file mode 100644 index 00000000..12c47497 --- /dev/null +++ b/.github/scripts/ai-provider/provider-factory.js @@ -0,0 +1,65 @@ +/** + * Provider Factory + * Creates the appropriate AI provider based on environment variables + */ + +const { createGitHubProvider } = require('./providers/github-models'); +const { createGeminiProvider } = require('./providers/gemini'); + +/** + * Get the active AI provider based on environment configuration + * + * Environment variables: + * - AI_PROVIDER: 'github' | 'gemini' | 'auto' (default: 'auto') + * - AI_MODEL: Override default model for the provider + * - GITHUB_TOKEN: API key for GitHub Models + * - GOOGLE_AI_API_KEY: API key for Gemini + * + * @returns {BaseAIProvider|null} Provider instance or null if none available + */ +function getProvider() { + const providerName = (process.env.AI_PROVIDER || 'auto').toLowerCase(); + const customModel = process.env.AI_MODEL; + + if (providerName === 'auto') { + return autoDetectProvider(customModel); + } + + switch (providerName) { + case 'github': + case 'github-models': + return createGitHubProvider(customModel); + + case 'gemini': + case 'google': + return createGeminiProvider(customModel); + + default: + console.warn(`⚠️ Unknown provider: ${providerName}. Falling back to auto-detect.`); + return autoDetectProvider(customModel); + } +} + +/** + * Auto-detect provider based on available API keys + */ +function autoDetectProvider(customModel) { + // Try Gemini + const geminiProvider = createGeminiProvider(customModel); + if (geminiProvider) { + return geminiProvider; + } + + // Fallback to GitHub Models (free in GitHub Actions) + const githubProvider = createGitHubProvider(customModel); + if (githubProvider) { + return githubProvider; + } + + return null; +} + +module.exports = { + getProvider, +}; + diff --git a/.github/scripts/ai-provider/providers/base-provider.js b/.github/scripts/ai-provider/providers/base-provider.js new file mode 100644 index 00000000..44df3dc0 --- /dev/null +++ b/.github/scripts/ai-provider/providers/base-provider.js @@ -0,0 +1,64 @@ +/** + * Base AI Provider class + * All provider implementations should extend this class + */ +class BaseAIProvider { + constructor(name, config, apiKey) { + this.name = name; + this.config = config; + this.apiKey = apiKey; + } + + /** + * Get maximum output tokens for this provider + */ + getMaxTokens() { + return this.config.maxTokens || 2500; + } + + /** + * Get rate limits for this provider + */ + getRateLimits() { + return { + maxRequestsPerMinute: this.config.maxRequestsPerMinute || 10, + maxTokensPerMinute: this.config.maxTokensPerMinute || 40000, + }; + } + + /** + * Build HTTP request options + * Must be implemented by subclass + */ + buildRequestOptions() { + throw new Error('buildRequestOptions must be implemented by subclass'); + } + + /** + * Build request body with prompts + * Must be implemented by subclass + */ + buildRequestBody(systemPrompt, userPrompt, maxTokens) { + throw new Error('buildRequestBody must be implemented by subclass'); + } + + /** + * Extract content and token usage from API response + * Must be implemented by subclass + * @returns {{content: string, tokens: number|null}|null} + */ + extractContent(response) { + throw new Error('extractContent must be implemented by subclass'); + } + + /** + * Check if error is a rate limit error + */ + isRateLimitError(error) { + const msg = error?.message || ''; + return msg.includes('429') || msg.toLowerCase().includes('rate limit'); + } +} + +module.exports = BaseAIProvider; + diff --git a/.github/scripts/ai-provider/providers/gemini.js b/.github/scripts/ai-provider/providers/gemini.js new file mode 100644 index 00000000..7564e0f5 --- /dev/null +++ b/.github/scripts/ai-provider/providers/gemini.js @@ -0,0 +1,94 @@ +/** + * Google AI (Gemini) Provider + * Uses Google AI API key for authentication + */ +const BaseAIProvider = require('./base-provider'); + +class GeminiProvider extends BaseAIProvider { + constructor(config, apiKey) { + const model = config.model || 'gemini-1.5-flash'; + super(`Google AI (${model})`, config, apiKey); + this.model = model; + } + + buildRequestOptions() { + return { + hostname: 'generativelanguage.googleapis.com', + port: 443, + path: `/v1beta/models/${this.model}:generateContent?key=${this.apiKey}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'User-Agent': 'Compose-CI/1.0', + }, + }; + } + + buildRequestBody(systemPrompt, userPrompt, maxTokens) { + // Gemini combines system and user prompts + const combinedPrompt = `${systemPrompt}\n\n${userPrompt}`; + + return JSON.stringify({ + contents: [{ + parts: [{ text: combinedPrompt }] + }], + generationConfig: { + maxOutputTokens: maxTokens || this.getMaxTokens(), + temperature: 0.7, + topP: 0.95, + topK: 40, + }, + safetySettings: [ + { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" }, + { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" }, + { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" }, + { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" } + ] + }); + } + + extractContent(response) { + const text = response.candidates?.[0]?.content?.parts?.[0]?.text; + if (text) { + return { + content: text, + tokens: response.usageMetadata?.totalTokenCount || null, + }; + } + return null; + } + + getRateLimits() { + return { + maxRequestsPerMinute: 15, + maxTokensPerMinute: 1000000, // 1M tokens per minute + }; + } + + +} + +/** + * Create Gemini provider + */ +function createGeminiProvider(customModel) { + const apiKey = process.env.GOOGLE_AI_API_KEY; + if (!apiKey) { + return null; + } + + const config = { + model: customModel || 'gemini-1.5-flash', + maxTokens: 2500, + maxRequestsPerMinute: 15, + maxTokensPerMinute: 1000000, + }; + + return new GeminiProvider(config, apiKey); +} + +module.exports = { + GeminiProvider, + createGeminiProvider, +}; + diff --git a/.github/scripts/ai-provider/providers/github-models.js b/.github/scripts/ai-provider/providers/github-models.js new file mode 100644 index 00000000..4eb1a056 --- /dev/null +++ b/.github/scripts/ai-provider/providers/github-models.js @@ -0,0 +1,82 @@ +/** + * GitHub Models (Azure OpenAI) Provider + * Uses GitHub token for authentication in GitHub Actions + */ +const BaseAIProvider = require('./base-provider'); + +class GitHubModelsProvider extends BaseAIProvider { + constructor(config, apiKey) { + const model = config.model || 'gpt-4o'; + super(`GitHub Models (${model})`, config, apiKey); + this.model = model; + } + + buildRequestOptions() { + return { + hostname: 'models.inference.ai.azure.com', + port: 443, + path: '/chat/completions', + method: 'POST', + headers: { + 'Authorization': `Bearer ${this.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'User-Agent': 'Compose-CI/1.0', + }, + }; + } + + buildRequestBody(systemPrompt, userPrompt, maxTokens) { + return JSON.stringify({ + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userPrompt }, + ], + model: this.model, + max_tokens: maxTokens || this.getMaxTokens(), + temperature: 0.7, + }); + } + + extractContent(response) { + if (response.choices?.[0]?.message?.content) { + return { + content: response.choices[0].message.content, + tokens: response.usage?.total_tokens || null, + }; + } + return null; + } + + getRateLimits() { + return { + maxRequestsPerMinute: 10, + maxTokensPerMinute: 40000, + }; + } +} + +/** + * Create GitHub Models provider + */ +function createGitHubProvider(customModel) { + const apiKey = process.env.GITHUB_TOKEN; + if (!apiKey) { + return null; + } + + const config = { + model: customModel || 'gpt-4o', + maxTokens: 2500, + maxRequestsPerMinute: 10, + maxTokensPerMinute: 40000, + }; + + return new GitHubModelsProvider(config, apiKey); +} + +module.exports = { + GitHubModelsProvider, + createGitHubProvider, +}; + diff --git a/.github/scripts/ai-provider/rate-limiter.js b/.github/scripts/ai-provider/rate-limiter.js new file mode 100644 index 00000000..df1d9c3f --- /dev/null +++ b/.github/scripts/ai-provider/rate-limiter.js @@ -0,0 +1,147 @@ +/** + * Rate Limiter + * Handles request-based and token-based rate limiting + */ + +class RateLimiter { + constructor() { + this.provider = null; + this.lastCallTime = 0; + this.tokenHistory = []; + this.limits = { + maxRequestsPerMinute: 10, + maxTokensPerMinute: 40000, + }; + this.tokenWindowMs = 60000; // 60 seconds + this.safetyMargin = 0.85; // Use 85% of token budget + } + + /** + * Set the active provider and update rate limits + */ + setProvider(provider) { + this.provider = provider; + this.limits = provider.getRateLimits(); + } + + /** + * Estimate token usage for a request + * Uses rough heuristic: ~4 characters per token + */ + estimateTokenUsage(systemPrompt, userPrompt, maxTokens) { + const inputText = (systemPrompt || '') + (userPrompt || ''); + const estimatedInputTokens = Math.ceil(inputText.length / 4); + return estimatedInputTokens + (maxTokens || 0); + } + + /** + * Wait for rate limits before making a request + */ + async waitForRateLimit(estimatedTokens) { + const now = Date.now(); + + // 1. Request-based rate limit (requests per minute) + const minDelayMs = Math.ceil(60000 / this.limits.maxRequestsPerMinute); + const elapsed = now - this.lastCallTime; + + if (this.lastCallTime > 0 && elapsed < minDelayMs) { + const waitTime = minDelayMs - elapsed; + console.log(` ⏳ Rate limit: waiting ${Math.ceil(waitTime / 1000)}s...`); + await this._sleep(waitTime); + } + + // 2. Token-based rate limit + this._cleanTokenHistory(); + const currentConsumption = this._getCurrentTokenConsumption(); + const effectiveBudget = this.limits.maxTokensPerMinute * this.safetyMargin; + const availableTokens = effectiveBudget - currentConsumption; + + if (estimatedTokens > availableTokens) { + const waitTime = this._calculateTokenWaitTime(estimatedTokens, currentConsumption); + if (waitTime > 0) { + console.log( + ` ⏳ Token budget: ${currentConsumption.toFixed(0)}/${effectiveBudget.toFixed(0)} used. ` + + `Waiting ${Math.ceil(waitTime / 1000)}s...` + ); + await this._sleep(waitTime); + this._cleanTokenHistory(); + } + } else { + console.log( + ` 📊 Token budget: ${currentConsumption.toFixed(0)}/${effectiveBudget.toFixed(0)} used, ` + + `~${estimatedTokens} needed` + ); + } + + this.lastCallTime = Date.now(); + } + + /** + * Record actual token consumption after a request + */ + recordTokenConsumption(tokens) { + this.tokenHistory.push({ + timestamp: Date.now(), + tokens: tokens, + }); + this._cleanTokenHistory(); + } + + /** + * Clean expired entries from token history + */ + _cleanTokenHistory() { + const now = Date.now(); + this.tokenHistory = this.tokenHistory.filter( + entry => (now - entry.timestamp) < this.tokenWindowMs + ); + } + + /** + * Get current token consumption in the rolling window + */ + _getCurrentTokenConsumption() { + return this.tokenHistory.reduce((sum, entry) => sum + entry.tokens, 0); + } + + /** + * Calculate how long to wait for token budget to free up + */ + _calculateTokenWaitTime(tokensNeeded, currentConsumption) { + const effectiveBudget = this.limits.maxTokensPerMinute * this.safetyMargin; + const availableTokens = effectiveBudget - currentConsumption; + + if (tokensNeeded <= availableTokens) { + return 0; + } + + if (this.tokenHistory.length === 0) { + return 0; + } + + // Find how many tokens need to expire + const tokensToFree = tokensNeeded - availableTokens; + let freedTokens = 0; + let oldestTimestamp = Date.now(); + + for (const entry of this.tokenHistory) { + freedTokens += entry.tokens; + oldestTimestamp = entry.timestamp; + + if (freedTokens >= tokensToFree) { + break; + } + } + + // Calculate wait time until that entry expires + const timeUntilExpiry = this.tokenWindowMs - (Date.now() - oldestTimestamp); + return Math.max(0, timeUntilExpiry + 2000); // Add 2s buffer + } + + async _sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} + +module.exports = RateLimiter; + diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index 0c7761b8..793e684d 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -1,23 +1,11 @@ /** - * GitHub Models integration for documentation enhancement - * Uses Azure AI inference endpoint with GitHub token auth - * See: https://github.blog/changelog/2025-04-14-github-actions-token-integration-now-generally-available-in-github-models/ + * AI-powered documentation enhancement + * Uses the ai-provider service for multi-provider support */ const fs = require('fs'); const path = require('path'); -const { models: MODELS_CONFIG } = require('./config'); -const { sleep, makeHttpsRequest } = require('../workflow-utils'); -const { - estimateTokenUsage, - waitForRateLimit, - recordTokenConsumption, - updateLastTokenConsumption, - calculate429WaitTime, -} = require('./token-rate-limiter'); - -// Maximum number of times to retry a single request after hitting a 429 -const MAX_RATE_LIMIT_RETRIES = 3; +const ai = require('../ai-provider'); const AI_PROMPT_PATH = path.join(__dirname, '../../docs-gen-prompts.md'); const REPO_INSTRUCTIONS_PATH = path.join(__dirname, '../../copilot-instructions.md'); @@ -319,191 +307,44 @@ function extractJSON(content) { } /** - * Enhance documentation data using GitHub Copilot + * Enhance documentation data using AI * @param {object} data - Parsed documentation data * @param {'module' | 'facet'} contractType - Type of contract - * @param {string} token - GitHub token + * @param {string} token - Legacy token parameter (deprecated, uses env vars now) * @returns {Promise} Enhanced data */ async function enhanceWithAI(data, contractType, token) { - if (!token) { - console.log(' ⚠️ No GitHub token provided, skipping AI enhancement'); - return addFallbackContent(data, contractType); - } - - const systemPrompt = buildSystemPrompt(); - const userPrompt = buildPrompt(data, contractType); - const maxTokens = MODELS_CONFIG.maxTokens; - - // Estimate token usage for this request (for rate limiting) - const estimatedTokens = estimateTokenUsage(systemPrompt, userPrompt, maxTokens); - - const requestBody = JSON.stringify({ - messages: [ - { - role: 'system', - content: systemPrompt, - }, - { - role: 'user', - content: userPrompt, - }, - ], - model: MODELS_CONFIG.model, - max_tokens: maxTokens, - }); - - // GitHub Models uses Azure AI inference endpoint - // Authentication: GITHUB_TOKEN works directly in GitHub Actions - const options = { - hostname: MODELS_CONFIG.host, - port: 443, - path: '/chat/completions', - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'User-Agent': 'Compose-DocGen/1.0', - }, - }; - try { - // Wait for both time-based and token-based rate limits - await waitForRateLimit(estimatedTokens); - - // Helper to handle a single API call and common response parsing - const performApiCall = async () => { - const response = await makeHttpsRequest(options, requestBody); - - // Only record token consumption if we got a successful response - // (not a 429 or other error that would throw before reaching here) - if (response.choices && response.choices[0] && response.choices[0].message) { - // Record token consumption (use actual if available, otherwise estimated) - if (response.usage && response.usage.total_tokens) { - // GitHub Models API provides actual usage - use it for more accurate tracking - const actualTokens = response.usage.total_tokens; - recordTokenConsumption(actualTokens); - console.log(` 📊 Actual token usage: ${actualTokens} tokens`); - } else { - // Fallback to estimated tokens if API doesn't provide usage - recordTokenConsumption(estimatedTokens); - console.log(` 📊 Estimated token usage: ${estimatedTokens} tokens`); - } - - const content = response.choices[0].message.content; - - // Debug: Log full response content - console.log(' 📋 Full API response content:'); - console.log(' ' + '='.repeat(80)); - console.log(content); - console.log(' ' + '='.repeat(80)); - console.log(' Response length:', content.length, 'chars'); - console.log(' First 100 chars:', JSON.stringify(content.substring(0, 100))); - console.log(' Last 100 chars:', JSON.stringify(content.substring(Math.max(0, content.length - 100)))); - - try { - // First, try to parse directly (most responses should be valid JSON) - let enhanced; - try { - enhanced = JSON.parse(content); - console.log('✅ AI enhancement successful (direct parse)'); - } catch (directParseError) { - // If direct parse fails, try to extract and clean JSON - console.log(' Direct parse failed, attempting to extract JSON...'); - const cleanedContent = extractJSON(content); - console.log(' Cleaned content length:', cleanedContent.length, 'chars'); - - enhanced = JSON.parse(cleanedContent); - console.log('✅ AI enhancement successful (after extraction)'); - } - - return convertEnhancedFields(enhanced, data); - } catch (parseError) { - console.log(' ⚠️ Could not parse API response as JSON'); - console.log(' Parse error:', parseError.message); - - // As a last resort, try one more time with extraction - try { - const cleanedContent = extractJSON(content); - console.log(' Attempting final parse with extracted JSON...'); - const enhanced = JSON.parse(cleanedContent); - console.log('✅ AI enhancement successful (final attempt with extraction)'); - - return convertEnhancedFields(enhanced, data); - } catch (finalError) { - console.log(' Final parse attempt also failed:', finalError.message); - console.log(' Error position:', finalError.message.match(/position (\d+)/)?.[1] || 'unknown'); - - // Show debugging info - try { - const cleanedContent = extractJSON(content); - console.log(' Cleaned content (first 500 chars):', JSON.stringify(cleanedContent.substring(0, 500))); - console.log(' Cleaned content (last 500 chars):', JSON.stringify(cleanedContent.substring(Math.max(0, cleanedContent.length - 500)))); - - // Try to find the error position in cleaned content - const errorPosMatch = finalError.message.match(/position (\d+)/); - if (errorPosMatch) { - const errorPos = parseInt(errorPosMatch[1], 10); - const start = Math.max(0, errorPos - 50); - const end = Math.min(cleanedContent.length, errorPos + 50); - console.log(' Error context (chars ' + start + '-' + end + '):', JSON.stringify(cleanedContent.substring(start, end))); - } - } catch (e) { - console.log(' Could not extract/clean content:', e.message); - } - } - - return addFallbackContent(data, contractType); - } - } + console.log(` AI Content Enhancement: ${data.title}`); + + const systemPrompt = buildSystemPrompt(); + const userPrompt = buildPrompt(data, contractType); - console.log(' ⚠️ Unexpected API response format'); - console.log(' Response structure:', JSON.stringify(response, null, 2).substring(0, 1000)); - return addFallbackContent(data, contractType); - }; - - let attempt = 0; - // Retry loop for handling minute-token 429s while still eventually - // progressing through all files in the run. - // We calculate the exact wait time based on when tokens will be available. - // eslint-disable-next-line no-constant-condition - while (true) { - try { - return await performApiCall(); - } catch (error) { - const msg = error && error.message ? error.message : ''; - - // Detect GitHub Models minute-token rate limit (HTTP 429) - if (msg.startsWith('HTTP 429:') && attempt < MAX_RATE_LIMIT_RETRIES) { - attempt += 1; - - // Calculate smart wait time based on token budget - const waitTime = calculate429WaitTime(estimatedTokens); - const waitSeconds = Math.ceil(waitTime / 1000); - - console.log( - ` ⚠️ GitHub Models rate limit reached (minute tokens). ` + - `Waiting ${waitSeconds}s for token budget to reset (retry ${attempt}/${MAX_RATE_LIMIT_RETRIES})...` - ); - await sleep(waitTime); - - // Re-check rate limit before retrying - await waitForRateLimit(estimatedTokens); - continue; - } - - if (msg.startsWith('HTTP 429:')) { - console.log(' ⚠️ Rate limit persisted after maximum retries, using fallback content'); - } else { - console.log(` ⚠️ GitHub Models API error: ${msg}`); - } - - return addFallbackContent(data, contractType); + // Call AI provider + const responseText = await ai.call(systemPrompt, userPrompt, { + onSuccess: (text, tokens) => { + console.log(` ✅ AI enhancement complete (${tokens} tokens)`); + }, + onError: (error) => { + console.log(` ⚠️ AI call failed: ${error.message}`); } + }); + + // Parse JSON response + let enhanced; + try { + enhanced = JSON.parse(responseText); + console.log(' ✅ JSON parsed successfully'); + } catch (directParseError) { + const cleanedContent = extractJSON(responseText); + enhanced = JSON.parse(cleanedContent); + console.log(' ✅ JSON extracted and parsed'); } - } catch (outerError) { - console.log(` ⚠️ GitHub Models API error (outer): ${outerError.message}`); + + return convertEnhancedFields(enhanced, data); + + } catch (error) { + console.log(` ⚠️ Enhancement failed for ${data.title}: ${error.message}`); return addFallbackContent(data, contractType); } } @@ -517,7 +358,7 @@ async function enhanceWithAI(data, contractType, token) { function addFallbackContent(data, contractType) { console.log(' Using fallback content'); - const enhanced = { ...data }; + const enhanced = { ...data } if (contractType === 'module') { enhanced.integrationNotes = AI_PROMPTS.moduleFallback.integrationNotes || diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index f314a131..b8aaa673 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -14,19 +14,5 @@ module.exports = { modulesOutputDir: 'website/docs/contracts/modules', // Template settings - defaultSidebarPosition: 99, - - // GitHub Models API settings (for optional AI enhancement) - // Uses Azure AI inference endpoint with GitHub token auth in Actions - // See: https://github.blog/changelog/2025-04-14-github-actions-token-integration-now-generally-available-in-github-models/ - models: { - host: 'models.inference.ai.azure.com', - model: 'gpt-4o', - // Balanced setting for quality documentation while respecting rate limits - // Token-aware rate limiting (token-rate-limiter.js) ensures we stay within - // the 40k tokens/minute limit. Setting to 2500 allows ~10-12 requests/minute - // with typical prompt sizes, providing good quality without hitting limits. - maxTokens: 2500, - }, + defaultSidebarPosition: 99 }; - diff --git a/.github/scripts/generate-docs-utils/token-rate-limiter.js b/.github/scripts/generate-docs-utils/token-rate-limiter.js deleted file mode 100644 index 19318e89..00000000 --- a/.github/scripts/generate-docs-utils/token-rate-limiter.js +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Token-Aware Rate Limiter for GitHub Models API - * - * Handles both time-based and token-based rate limiting to stay within: - * - 10 requests per 60 seconds (request rate limit) - * - 40,000 tokens per 60 seconds (minute-token limit) - * - Daily limits (requests and tokens per day) - * - */ - -const fs = require('fs'); -const path = require('path'); -const { sleep } = require('../workflow-utils'); - -// GitHub Models API Limits -const MAX_REQUESTS_PER_MINUTE = 10; -const MAX_TOKENS_PER_MINUTE = 40000; - -// Daily limits (conservative estimates - adjust based on your actual tier) -// Free tier typically has lower limits, paid tiers have higher -const MAX_REQUESTS_PER_DAY = 1500; // Conservative estimate -const MAX_TOKENS_PER_DAY = 150000; // Conservative estimate - -// Safety margins to avoid hitting limits -const REQUEST_SAFETY_BUFFER_MS = 1000; // Add 1s buffer to request spacing -const TOKEN_BUDGET_SAFETY_MARGIN = 0.85; // Use 85% of minute budget -const DAILY_BUDGET_SAFETY_MARGIN = 0.90; // Use 90% of daily budget - -// Calculated values -const REQUEST_DELAY_MS = Math.ceil((60000 / MAX_REQUESTS_PER_MINUTE) + REQUEST_SAFETY_BUFFER_MS); -const EFFECTIVE_TOKEN_BUDGET = MAX_TOKENS_PER_MINUTE * TOKEN_BUDGET_SAFETY_MARGIN; -const EFFECTIVE_DAILY_REQUESTS = Math.floor(MAX_REQUESTS_PER_DAY * DAILY_BUDGET_SAFETY_MARGIN); -const EFFECTIVE_DAILY_TOKENS = Math.floor(MAX_TOKENS_PER_DAY * DAILY_BUDGET_SAFETY_MARGIN); -const TOKEN_WINDOW_MS = 60000; // 60 second rolling window - -// State tracking (minute-level) -let lastApiCallTime = 0; -let tokenConsumptionHistory = []; // Array of { timestamp, tokens } - -// Daily tracking -const DAILY_USAGE_FILE = path.join(__dirname, '.daily-usage.json'); -let dailyUsage = loadDailyUsage(); - -/** - * Load daily usage from file - * @returns {object} Daily usage data - */ -function loadDailyUsage() { - try { - if (fs.existsSync(DAILY_USAGE_FILE)) { - const data = JSON.parse(fs.readFileSync(DAILY_USAGE_FILE, 'utf8')); - const today = new Date().toISOString().split('T')[0]; - - // Reset if it's a new day - if (data.date !== today) { - return { date: today, requests: 0, tokens: 0 }; - } - - return data; - } - } catch (error) { - console.warn('Could not load daily usage file:', error.message); - } - - // Default: new day - return { - date: new Date().toISOString().split('T')[0], - requests: 0, - tokens: 0, - }; -} - -/** - * Save daily usage to file - */ -function saveDailyUsage() { - try { - fs.writeFileSync(DAILY_USAGE_FILE, JSON.stringify(dailyUsage, null, 2)); - } catch (error) { - console.warn('Could not save daily usage file:', error.message); - } -} - -/** - * Check if daily limits would be exceeded - * @param {number} estimatedTokens - Tokens needed for next request - * @returns {object} { exceeded: boolean, reason: string } - */ -function checkDailyLimits(estimatedTokens) { - const today = new Date().toISOString().split('T')[0]; - - // Reset if new day - if (dailyUsage.date !== today) { - dailyUsage = { date: today, requests: 0, tokens: 0 }; - saveDailyUsage(); - } - - // Check request limit - if (dailyUsage.requests >= EFFECTIVE_DAILY_REQUESTS) { - return { - exceeded: true, - reason: `Daily request limit reached (${dailyUsage.requests}/${EFFECTIVE_DAILY_REQUESTS})`, - }; - } - - // Check token limit - if (dailyUsage.tokens + estimatedTokens > EFFECTIVE_DAILY_TOKENS) { - return { - exceeded: true, - reason: `Daily token limit would be exceeded (${dailyUsage.tokens + estimatedTokens}/${EFFECTIVE_DAILY_TOKENS})`, - }; - } - - return { exceeded: false }; -} - -/** - * Record daily usage - * @param {number} tokens - Tokens consumed - */ -function recordDailyUsage(tokens) { - const today = new Date().toISOString().split('T')[0]; - - // Reset if new day - if (dailyUsage.date !== today) { - dailyUsage = { date: today, requests: 0, tokens: 0 }; - } - - dailyUsage.requests += 1; - dailyUsage.tokens += tokens; - saveDailyUsage(); -} - -/** - * Estimate token usage for a request - * Uses a rough heuristic: ~4 characters per token for input text - * @param {string} systemPrompt - System prompt text - * @param {string} userPrompt - User prompt text - * @param {number} maxTokens - Max tokens requested for completion - * @returns {number} Estimated total tokens (input + output) - */ -function estimateTokenUsage(systemPrompt, userPrompt, maxTokens) { - const inputText = (systemPrompt || '') + (userPrompt || ''); - // Rough estimate: ~4 characters per token for GPT-4 models - const estimatedInputTokens = Math.ceil(inputText.length / 4); - // Add max_tokens for potential output (worst case: we use all requested tokens) - return estimatedInputTokens + maxTokens; -} - -/** - * Clean expired entries from token consumption history - * Removes entries older than TOKEN_WINDOW_MS - */ -function cleanTokenHistory() { - const now = Date.now(); - tokenConsumptionHistory = tokenConsumptionHistory.filter( - entry => (now - entry.timestamp) < TOKEN_WINDOW_MS - ); -} - -/** - * Get current token consumption in the rolling window - * @returns {number} Total tokens consumed in the last 60 seconds - */ -function getCurrentTokenConsumption() { - cleanTokenHistory(); - return tokenConsumptionHistory.reduce((sum, entry) => sum + entry.tokens, 0); -} - -/** - * Record token consumption for rate limiting - * @param {number} tokens - Tokens consumed in the request - */ -function recordTokenConsumption(tokens) { - tokenConsumptionHistory.push({ - timestamp: Date.now(), - tokens: tokens, - }); - cleanTokenHistory(); -} - -/** - * Update the last recorded token consumption with actual usage from API response - * @param {number} actualTokens - Actual tokens used (from API response) - */ -function updateLastTokenConsumption(actualTokens) { - if (tokenConsumptionHistory.length > 0) { - const lastEntry = tokenConsumptionHistory[tokenConsumptionHistory.length - 1]; - lastEntry.tokens = actualTokens; - } -} - -/** - * Calculate wait time needed for token budget to free up - * @param {number} tokensNeeded - Tokens needed for the next request - * @param {number} currentConsumption - Current token consumption - * @returns {number} Milliseconds to wait (0 if no wait needed) - */ -function calculateTokenWaitTime(tokensNeeded, currentConsumption) { - const availableTokens = EFFECTIVE_TOKEN_BUDGET - currentConsumption; - - if (tokensNeeded <= availableTokens) { - return 0; // No wait needed - } - - // Need to wait for some tokens to expire from the rolling window - if (tokenConsumptionHistory.length === 0) { - return 0; // No history, shouldn't happen but handle gracefully - } - - // Find how many tokens need to expire - const tokensToFree = tokensNeeded - availableTokens; - let freedTokens = 0; - let oldestTimestamp = Date.now(); - - // Walk through history from oldest to newest - for (const entry of tokenConsumptionHistory) { - freedTokens += entry.tokens; - oldestTimestamp = entry.timestamp; - - if (freedTokens >= tokensToFree) { - break; - } - } - - // Calculate wait time until that entry expires - const now = Date.now(); - const timeUntilExpiry = TOKEN_WINDOW_MS - (now - oldestTimestamp); - - // Add small buffer to ensure the tokens have actually expired - return Math.max(0, timeUntilExpiry + 2000); -} - -/** - * Calculate wait time for 429 rate limit recovery - * When we hit a 429, we need to wait for enough tokens to free up from the window - * @param {number} tokensNeeded - Tokens needed for the next request - * @returns {number} Milliseconds to wait - */ -function calculate429WaitTime(tokensNeeded) { - cleanTokenHistory(); - const currentConsumption = getCurrentTokenConsumption(); - const availableTokens = EFFECTIVE_TOKEN_BUDGET - currentConsumption; - - if (tokensNeeded <= availableTokens) { - // We should have budget, but got 429 anyway - wait for oldest entry to expire - if (tokenConsumptionHistory.length > 0) { - const oldestEntry = tokenConsumptionHistory[0]; - const now = Date.now(); - const timeUntilExpiry = TOKEN_WINDOW_MS - (now - oldestEntry.timestamp); - return Math.max(5000, timeUntilExpiry + 2000); // At least 5s, plus buffer - } - return 10000; // Default 10s if no history - } - - // Calculate how long until we have enough budget - return calculateTokenWaitTime(tokensNeeded, currentConsumption); -} - -/** - * Wait for rate limits if needed (both time-based and token-based) - * This is the main entry point for rate limiting before making an API call - * - * @param {number} estimatedTokens - Estimated tokens for the upcoming request - * @returns {Promise} - */ -async function waitForRateLimit(estimatedTokens) { - const now = Date.now(); - - // 1. Check time-based rate limit (requests per minute) - const elapsed = now - lastApiCallTime; - if (lastApiCallTime > 0 && elapsed < REQUEST_DELAY_MS) { - const waitTime = REQUEST_DELAY_MS - elapsed; - console.log(` ⏳ Rate limit: waiting ${Math.ceil(waitTime / 1000)}s (request spacing)...`); - await sleep(waitTime); - } - - // 2. Check token-based rate limit - cleanTokenHistory(); - const currentConsumption = getCurrentTokenConsumption(); - const availableTokens = EFFECTIVE_TOKEN_BUDGET - currentConsumption; - - if (estimatedTokens > availableTokens) { - const waitTime = calculateTokenWaitTime(estimatedTokens, currentConsumption); - - if (waitTime > 0) { - console.log( - ` ⏳ Token budget: ${currentConsumption.toFixed(0)}/${EFFECTIVE_TOKEN_BUDGET.toFixed(0)} tokens used. ` + - `Need ${estimatedTokens} tokens. Waiting ${Math.ceil(waitTime / 1000)}s for budget to reset...` - ); - await sleep(waitTime); - cleanTokenHistory(); // Re-clean after waiting - } - } else { - const remainingTokens = availableTokens - estimatedTokens; - console.log( - ` 📊 Token budget: ${currentConsumption.toFixed(0)}/${EFFECTIVE_TOKEN_BUDGET.toFixed(0)} used, ` + - `~${estimatedTokens} needed, ~${remainingTokens.toFixed(0)} remaining after this request` - ); - } - - // Update last call time - lastApiCallTime = Date.now(); -} - -/** - * Get current rate limiter statistics (useful for debugging/monitoring) - * @returns {object} Statistics object - */ -function getStats() { - cleanTokenHistory(); - const currentConsumption = getCurrentTokenConsumption(); - - return { - requestDelayMs: REQUEST_DELAY_MS, - maxTokensPerMinute: MAX_TOKENS_PER_MINUTE, - effectiveTokenBudget: EFFECTIVE_TOKEN_BUDGET, - currentTokenConsumption: currentConsumption, - availableTokens: EFFECTIVE_TOKEN_BUDGET - currentConsumption, - tokenHistoryEntries: tokenConsumptionHistory.length, - lastApiCallTime: lastApiCallTime, - timeSinceLastCall: lastApiCallTime > 0 ? Date.now() - lastApiCallTime : null, - }; -} - -/** - * Reset rate limiter state (useful for testing) - */ -function reset() { - lastApiCallTime = 0; - tokenConsumptionHistory = []; -} - -module.exports = { - estimateTokenUsage, - waitForRateLimit, - recordTokenConsumption, - updateLastTokenConsumption, - getCurrentTokenConsumption, - getStats, - reset, - calculate429WaitTime, - // Export constants for testing/configuration - MAX_REQUESTS_PER_MINUTE, - MAX_TOKENS_PER_MINUTE, - EFFECTIVE_TOKEN_BUDGET, -}; - diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 6a5ddfb8..9d55945b 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -101,6 +101,8 @@ jobs: - name: Run documentation generator if: steps.changed-files.outputs.has_changes == 'true' env: + # AI Provider Configuration + GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SKIP_ENHANCEMENT: ${{ github.event.inputs.skip_enhancement || 'false' }} run: | From 35c4fb10b73eff0850482e188d0f1eca595cff2c Mon Sep 17 00:00:00 2001 From: MN Date: Tue, 16 Dec 2025 19:35:20 -0500 Subject: [PATCH 21/68] add debug logs --- .github/scripts/ai-provider/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/scripts/ai-provider/index.js b/.github/scripts/ai-provider/index.js index a2f97bd5..06350023 100644 --- a/.github/scripts/ai-provider/index.js +++ b/.github/scripts/ai-provider/index.js @@ -38,6 +38,9 @@ class AIProvider { ); } + console.log(`Using AI provider: ${this.provider.name}`); + console.log(`Provider model: ${this.provider.model}`); + this.rateLimiter.setProvider(this.provider); this.initialized = true; } From 272e7310357f1e3522c660095050bc68722e3cd2 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 13:20:56 -0500 Subject: [PATCH 22/68] update names --- .github/workflows/{docs.yml => docs-build.yml} | 2 +- .../{generate-docs.yml => docs-generate.yml} | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) rename .github/workflows/{docs.yml => docs-build.yml} (98%) rename .github/workflows/{generate-docs.yml => docs-generate.yml} (92%) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs-build.yml similarity index 98% rename from .github/workflows/docs.yml rename to .github/workflows/docs-build.yml index 96ed2116..541d9366 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs-build.yml @@ -1,4 +1,4 @@ -name: Documentation +name: Build Docs on: pull_request: diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/docs-generate.yml similarity index 92% rename from .github/workflows/generate-docs.yml rename to .github/workflows/docs-generate.yml index 9d55945b..52d9d7f3 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/docs-generate.yml @@ -1,4 +1,4 @@ -name: Generate Facets & Modules Docs +name: Generate Docs on: push: @@ -33,6 +33,16 @@ jobs: runs-on: ubuntu-latest steps: + - name: Debug AI Provider Configuration + if: steps.changed-files.outputs.has_changes == 'true' + env: + GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "GOOGLE_AI_API_KEY is set: $( [ -n \"$GOOGLE_AI_API_KEY\" ] && echo 'YES' || echo 'NO' )" + echo "GITHUB_TOKEN is set: $( [ -n \"$GITHUB_TOKEN\" ] && echo 'YES' || echo 'NO' )" + echo "GOOGLE_AI_API_KEY length: ${#GOOGLE_AI_API_KEY}" + - name: Checkout code uses: actions/checkout@v4 with: From d0605881f8d16f1f625effb1b0c35718d737cd70 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 13:25:15 -0500 Subject: [PATCH 23/68] remove condition to debug --- .github/workflows/docs-generate.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docs-generate.yml b/.github/workflows/docs-generate.yml index 52d9d7f3..afc9e1f8 100644 --- a/.github/workflows/docs-generate.yml +++ b/.github/workflows/docs-generate.yml @@ -34,7 +34,6 @@ jobs: steps: - name: Debug AI Provider Configuration - if: steps.changed-files.outputs.has_changes == 'true' env: GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 6981272d721b62926bcea76bc359a7a853240519 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 13:40:15 -0500 Subject: [PATCH 24/68] remove secret debug step --- .github/workflows/docs-generate.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/docs-generate.yml b/.github/workflows/docs-generate.yml index afc9e1f8..d41c7f04 100644 --- a/.github/workflows/docs-generate.yml +++ b/.github/workflows/docs-generate.yml @@ -32,16 +32,7 @@ jobs: name: Generate Pages runs-on: ubuntu-latest - steps: - - name: Debug AI Provider Configuration - env: - GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "GOOGLE_AI_API_KEY is set: $( [ -n \"$GOOGLE_AI_API_KEY\" ] && echo 'YES' || echo 'NO' )" - echo "GITHUB_TOKEN is set: $( [ -n \"$GITHUB_TOKEN\" ] && echo 'YES' || echo 'NO' )" - echo "GOOGLE_AI_API_KEY length: ${#GOOGLE_AI_API_KEY}" - + steps: - name: Checkout code uses: actions/checkout@v4 with: From d090880b772cc365cab5afd0af1c5afe079a7b2e Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 13:53:35 -0500 Subject: [PATCH 25/68] change default gemini model --- .github/scripts/ai-provider/index.js | 5 +++-- .../ai-provider/providers/base-provider.js | 4 ++++ .github/scripts/ai-provider/providers/gemini.js | 17 ++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/scripts/ai-provider/index.js b/.github/scripts/ai-provider/index.js index 06350023..f982baaa 100644 --- a/.github/scripts/ai-provider/index.js +++ b/.github/scripts/ai-provider/index.js @@ -38,8 +38,9 @@ class AIProvider { ); } - console.log(`Using AI provider: ${this.provider.name}`); - console.log(`Provider model: ${this.provider.model}`); + console.log(`====================================================`); + console.log(` ✨ Using AI provider: ${this.provider.name}`); + console.log(`====================================================`); this.rateLimiter.setProvider(this.provider); this.initialized = true; diff --git a/.github/scripts/ai-provider/providers/base-provider.js b/.github/scripts/ai-provider/providers/base-provider.js index 44df3dc0..fe23fb1c 100644 --- a/.github/scripts/ai-provider/providers/base-provider.js +++ b/.github/scripts/ai-provider/providers/base-provider.js @@ -4,6 +4,10 @@ */ class BaseAIProvider { constructor(name, config, apiKey) { + if (!apiKey) { + throw new Error('API key is required'); + } + this.name = name; this.config = config; this.apiKey = apiKey; diff --git a/.github/scripts/ai-provider/providers/gemini.js b/.github/scripts/ai-provider/providers/gemini.js index 7564e0f5..07f7517d 100644 --- a/.github/scripts/ai-provider/providers/gemini.js +++ b/.github/scripts/ai-provider/providers/gemini.js @@ -4,9 +4,24 @@ */ const BaseAIProvider = require('./base-provider'); +/** + * Gemini Provider Class + * Default model: gemini-2.5-flash-lite + * This model is a lightweight model that is designed to be fast and efficient. + * Refer to https://ai.google.dev/gemini-api/docs for the list of models. + */ class GeminiProvider extends BaseAIProvider { + /** + * Constructor + * @param {object} config - Configuration object + * @param {string} config.model - Model to use + * @param {number} config.maxTokens - Maximum number of tokens to generate + * @param {number} config.maxRequestsPerMinute - Maximum number of requests per minute + * @param {number} config.maxTokensPerMinute - Maximum number of tokens per minute + * @param {string} apiKey - Google AI API key (required) + */ constructor(config, apiKey) { - const model = config.model || 'gemini-1.5-flash'; + const model = config.model || 'gemini-2.5-flash-lite'; super(`Google AI (${model})`, config, apiKey); this.model = model; } From 7cec432f52f2cad53f4d546df90deb49e6a66d41 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 14:07:26 -0500 Subject: [PATCH 26/68] remove redundant check and default models setting --- .github/scripts/ai-provider/providers/gemini.js | 6 ++---- .github/scripts/ai-provider/providers/github-models.js | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/scripts/ai-provider/providers/gemini.js b/.github/scripts/ai-provider/providers/gemini.js index 07f7517d..9af4f159 100644 --- a/.github/scripts/ai-provider/providers/gemini.js +++ b/.github/scripts/ai-provider/providers/gemini.js @@ -88,12 +88,10 @@ class GeminiProvider extends BaseAIProvider { */ function createGeminiProvider(customModel) { const apiKey = process.env.GOOGLE_AI_API_KEY; - if (!apiKey) { - return null; - } + const config = { - model: customModel || 'gemini-1.5-flash', + model: customModel, maxTokens: 2500, maxRequestsPerMinute: 15, maxTokensPerMinute: 1000000, diff --git a/.github/scripts/ai-provider/providers/github-models.js b/.github/scripts/ai-provider/providers/github-models.js index 4eb1a056..6d9426cc 100644 --- a/.github/scripts/ai-provider/providers/github-models.js +++ b/.github/scripts/ai-provider/providers/github-models.js @@ -61,12 +61,9 @@ class GitHubModelsProvider extends BaseAIProvider { */ function createGitHubProvider(customModel) { const apiKey = process.env.GITHUB_TOKEN; - if (!apiKey) { - return null; - } const config = { - model: customModel || 'gpt-4o', + model: customModel, maxTokens: 2500, maxRequestsPerMinute: 10, maxTokensPerMinute: 40000, From a9f26579832cf6b36ae605397439b8704f131982 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Fri, 19 Dec 2025 21:25:39 +0000 Subject: [PATCH 27/68] docs: auto-generate contracts docs from NatSpec --- .../contracts/facets/AccessControlFacet.mdx | 561 +++++++++++++ .../facets/AccessControlPausableFacet.mdx | 330 ++++++++ .../facets/AccessControlTemporalFacet.mdx | 461 +++++++++++ .../docs/contracts/facets/DiamondCutFacet.mdx | 425 ++++++++++ .../contracts/facets/DiamondLoupeFacet.mdx | 255 ++++++ .../docs/contracts/facets/ERC1155Facet.mdx | 671 ++++++++++++++++ .../contracts/facets/ERC20BridgeableFacet.mdx | 417 ++++++++++ .../docs/contracts/facets/ERC20BurnFacet.mdx | 276 +++++++ website/docs/contracts/facets/ERC20Facet.mdx | 594 ++++++++++++++ .../contracts/facets/ERC20PermitFacet.mdx | 337 ++++++++ .../docs/contracts/facets/ERC6909Facet.mdx | 529 +++++++++++++ .../docs/contracts/facets/ERC721BurnFacet.mdx | 221 ++++++ .../facets/ERC721EnumerableBurnFacet.mdx | 224 ++++++ .../facets/ERC721EnumerableFacet.mdx | 743 ++++++++++++++++++ website/docs/contracts/facets/ERC721Facet.mdx | 669 ++++++++++++++++ .../docs/contracts/facets/ExampleDiamond.mdx | 146 ++++ website/docs/contracts/facets/OwnerFacet.mdx | 212 +++++ .../contracts/facets/OwnerTwoStepsFacet.mdx | 287 +++++++ .../docs/contracts/facets/RoyaltyFacet.mdx | 211 +++++ .../contracts/modules/AccessControlMod.mdx | 447 +++++++++++ .../modules/AccessControlPausableMod.mdx | 384 +++++++++ .../modules/AccessControlTemporalMod.mdx | 484 ++++++++++++ .../docs/contracts/modules/DiamondCutMod.mdx | 385 +++++++++ website/docs/contracts/modules/DiamondMod.mdx | 236 ++++++ website/docs/contracts/modules/ERC1155Mod.mdx | 624 +++++++++++++++ website/docs/contracts/modules/ERC165Mod.mdx | 159 ++++ .../contracts/modules/ERC20BridgeableMod.mdx | 439 +++++++++++ website/docs/contracts/modules/ERC20Mod.mdx | 425 ++++++++++ .../docs/contracts/modules/ERC20PermitMod.mdx | 279 +++++++ website/docs/contracts/modules/ERC6909Mod.mdx | 535 +++++++++++++ .../contracts/modules/ERC721EnumerableMod.mdx | 353 +++++++++ website/docs/contracts/modules/ERC721Mod.mdx | 365 +++++++++ .../contracts/modules/NonReentrancyMod.mdx | 141 ++++ website/docs/contracts/modules/OwnerMod.mdx | 251 ++++++ .../contracts/modules/OwnerTwoStepsMod.mdx | 322 ++++++++ website/docs/contracts/modules/RoyaltyMod.mdx | 367 +++++++++ 36 files changed, 13765 insertions(+) create mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx create mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx create mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx create mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721Facet.mdx create mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx create mode 100644 website/docs/contracts/facets/OwnerFacet.mdx create mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx create mode 100644 website/docs/contracts/modules/AccessControlMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx create mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx create mode 100644 website/docs/contracts/modules/DiamondMod.mdx create mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx create mode 100644 website/docs/contracts/modules/ERC165Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx create mode 100644 website/docs/contracts/modules/ERC20Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx create mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx create mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx create mode 100644 website/docs/contracts/modules/ERC721Mod.mdx create mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx create mode 100644 website/docs/contracts/modules/OwnerMod.mdx create mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx create mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx new file mode 100644 index 00000000..bb44400e --- /dev/null +++ b/website/docs/contracts/facets/AccessControlFacet.mdx @@ -0,0 +1,561 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Contract documentation for AccessControlFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for AccessControlFacet + + + +- Role-based access control (RBAC) for granular permission management. +- Support for granting and revoking roles to individual accounts or batches. +- Ability to define and manage role hierarchies through `setRoleAdmin`. +- Built-in checks (`requireRole`) to enforce access control at the function call level. + + +## Overview + +The AccessControlFacet provides a robust, role-based access control (RBAC) system for Compose diamonds. It enables granular permission management, allowing administrators to grant, revoke, and manage roles for accounts, ensuring that sensitive operations can only be performed by authorized entities. This facet is fundamental for securing diamond functionality. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. error: AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. error: AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. error: AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondProxy, DiamondInit} from "@compose/diamond/contracts/Diamond.sol"; +import {AccessControlFacet} from "@compose/access-control/contracts/AccessControlFacet.sol"; + +contract MyDiamondInit is DiamondInit { + function init() public override { + // ... other initializations ... + + // Initialize AccessControlFacet + AccessControlFacet accessControl = AccessControlFacet(address(this)); + bytes32 adminRole = keccak256(abi.encodePacked("ROLE_ADMIN")); + bytes32 defaultAdminRole = accessControl.getRoleAdmin(adminRole); + + // Grant default admin role to the deployer + accessControl.grantRole(defaultAdminRole, msg.sender); + } +} + +contract MyDiamond is DiamondProxy { + // Assuming AccessControlFacet is deployed and added to the diamond + // ... + + function grantAdminRoleToUser(address _user) external { + AccessControlFacet accessControl = AccessControlFacet(address(this)); + bytes32 adminRole = keccak256(abi.encodePacked("ROLE_ADMIN")); + // Caller must have the admin role to grant other roles + accessControl.grantRole(adminRole, _user); + } + + function executeSensitiveOperation() external { + AccessControlFacet accessControl = AccessControlFacet(address(this)); + bytes32 sensitiveOperationRole = keccak256(abi.encodePacked("SENSITIVE_OPERATION_ROLE")); + // Require the caller to have the specific role + accessControl.requireRole(sensitiveOperationRole, msg.sender); + + // ... perform sensitive operation ... + } +}`} + + +## Best Practices + + +- Initialize roles and grant initial administrative privileges during diamond deployment via `DiamondInit`. +- Define distinct roles for different permission levels and grant them judiciously using `grantRole` or `grantRoleBatch`. +- Use `requireRole` extensively within facet functions to enforce access control checks before executing sensitive logic. + + +## Security Considerations + + +Ensure that the initial administrative roles are granted only to trusted addresses during deployment. Be cautious when granting broad roles, and always use `requireRole` at the entry point of functions that require specific permissions. The `renounceRole` function should be used with care, as it permanently removes the caller's access to a role. The caller must be the current admin of the role to set a new admin role via `setRoleAdmin`. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..7568e61e --- /dev/null +++ b/website/docs/contracts/facets/AccessControlPausableFacet.mdx @@ -0,0 +1,330 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Contract documentation for AccessControlPausableFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for AccessControlPausableFacet + + + +- Self-contained facet with no imports or inheritance +- Only `external` and `internal` function visibility +- Follows Compose readability-first conventions +- Ready for diamond integration + + +## Overview + +Documentation for AccessControlPausableFacet. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..fbd6669f --- /dev/null +++ b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx @@ -0,0 +1,461 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Contract documentation for AccessControlTemporalFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for AccessControlTemporalFacet + + + +- Grants roles with specific expiry timestamps, enabling temporary permissions. +- Provides functions to check if a role has expired (`isRoleExpired`). +- Allows for revocation of time-bound roles before their natural expiry (`revokeTemporalRole`). +- Enforces authorization checks, ensuring only role admins can manage temporal role assignments. + + +## Overview + +The AccessControlTemporalFacet extends the diamond's access control capabilities by introducing time-bound role assignments. It allows for granting and revoking roles with specific expiry timestamps, enhancing dynamic permission management within the diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; +import {IAccessControlTemporal} from "./interfaces/IAccessControlTemporal.sol"; + +contract Deployer { + address public diamondAddress; + + function deployDiamond() public { + // ... diamond deployment logic ... + diamondAddress = address(0xYourDiamondAddress); // Replace with actual diamond address + } + + function grantTemporaryRole() public { + IAccessControlTemporal(diamondAddress).grantRoleWithExpiry( + IAccessControlTemporal.Role.Admin, + address(0xUserAddress), // Replace with user address + block.timestamp + 3600 // Role expires in 1 hour + ); + } + + function checkRole() public view returns (bool) { + return IAccessControlTemporal(diamondAddress).isRoleExpired( + IAccessControlTemporal.Role.Admin, + address(0xUserAddress) // Replace with user address + ); + } + + function revokeRole() public { + IAccessControlTemporal(diamondAddress).revokeTemporalRole( + IAccessControlTemporal.Role.Admin, + address(0xUserAddress) // Replace with user address + ); + } +}`} + + +## Best Practices + + +- Initialize the `AccessControlTemporalFacet` with appropriate admin roles during diamond deployment. +- Ensure the `AccessControlFacet` is also deployed and configured for fundamental role management. +- Utilize `grantRoleWithExpiry` for temporary administrative access and `revokeTemporalRole` for immediate removal. + + +## Security Considerations + + +Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role manipulation. The `requireValidRole` function checks for both existence and expiry, mitigating risks associated with stale or expired permissions. Reentrancy is not a concern as these functions do not make external calls. Input validation is handled by the underlying access control mechanisms and explicit checks within the functions. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx new file mode 100644 index 00000000..2d794540 --- /dev/null +++ b/website/docs/contracts/facets/DiamondCutFacet.mdx @@ -0,0 +1,425 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Add=0, Replace=1, Remove=2" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Add=0, Replace=1, Remove=2 + + + +- Supports adding new functions by linking selectors to new facet addresses. +- Enables updating existing functionality by replacing old facet addresses with new ones for specific selectors. +- Allows removal of functions by unlinking selectors, effectively disabling them. +- Facilitates atomic upgrades by allowing a function to be executed immediately after the cut. + + +## Overview + +The DiamondCutFacet provides the core functionality for upgrading and managing the functions within a Compose diamond. It allows adding, replacing, and removing facet functions, enabling dynamic contract logic updates and feature extensibility. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +--- +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond/facets/DiamondCutFacet.sol"; + +contract DiamondDeployer { + // Assume diamond is already deployed and initialized + IDiamondCut immutable diamondCutFacet; + + constructor(address _diamondAddress) { + diamondCutFacet = IDiamondCut(_diamondAddress); + } + + function upgradeDiamond() external { + // Example: Add a new function + // address newFacetAddress = address(new MyNewFacet()); + // bytes32[] memory selectors = new bytes32[](1); + // selectors[0] = IDiamondCut.myNewFunction.selector; + // diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](0), newFacetAddress); + + // Example: Replace an existing function + // address updatedFacetAddress = address(new MyUpdatedFacet()); + // bytes32[] memory selectorsToReplace = new bytes32[](1); + // selectorsToReplace[0] = IDiamondCut.existingFunction.selector; + // diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](1), new IDiamondCut.FacetCut[](0), updatedFacetAddress); + + // Example: Remove a function + // bytes32[] memory selectorsToRemove = new bytes32[](1); + // selectorsToRemove[0] = IDiamondCut.deprecatedFunction.selector; + // diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](1), address(0)); // address(0) signifies removal + + // To execute a function after the cut, pass it in the last argument + // diamondCutFacet.diamondCut(..., abi.encodeCall(IDiamondCut.someFunction, (arg1, arg2))); + } +}`} + + +## Best Practices + + +- Ensure the `diamondCut` function is only callable by authorized addresses (e.g., an owner or a governance contract) to prevent unauthorized upgrades. +- Carefully manage the mapping of function selectors to facet addresses during upgrades to avoid breaking existing functionality or introducing vulnerabilities. +- When replacing or removing functions, consider the impact on dependent facets and external contracts interacting with the diamond. + + +## Security Considerations + + +Access to `diamondCut`, `addFunctions`, `replaceFunctions`, and `removeFunctions` must be strictly controlled. Unauthorized calls can lead to the diamond's functionality being compromised. Ensure that the `diamondCut` function is not susceptible to reentrancy if it includes an optional `delegatecall`. Input validation on function selectors and facet addresses is crucial. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..70eebdea --- /dev/null +++ b/website/docs/contracts/facets/DiamondLoupeFacet.mdx @@ -0,0 +1,255 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "The functions in DiamondLoupeFacet MUST be added to a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +The functions in DiamondLoupeFacet MUST be added to a diamond. + + + +- Provides comprehensive read-only access to the diamond's facet registry. +- Optimized for gas efficiency, especially in diamonds with numerous facets and selectors. +- Enables dynamic discovery of diamond functionality without prior knowledge of facet addresses. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows querying facet addresses, associated function selectors, and the overall facet structure of the diamond, enabling builders to understand and interact with the diamond's deployed functionality. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupeFacet} from "@compose/diamond-contracts/contracts/facets/DiamondLoupeFacet.sol"; + +contract DiamondLoupeConsumer { + IDiamondLoupeFacet diamondLoupeFacet; + + constructor(address _diamondAddress) { + diamondLoupeFacet = IDiamondLoupeFacet(_diamondAddress); + } + + function getAllFacets() public view returns (IDiamondLoupeFacet.Facet[] memory) { + return diamondLoupeFacet.facets(); + } + + function getFacetAddress(bytes4 _selector) public view returns (address) { + return diamondLoupeFacet.facetAddress(_selector); + } + + function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { + return diamondLoupeFacet.facetFunctionSelectors(_facet); + } +}`} + + +## Best Practices + + +- Integrate `DiamondLoupeFacet` into your diamond to provide necessary introspection for developers and external tools. +- Call loupe functions with `view` or `pure` modifiers to avoid unnecessary gas costs. +- Cache frequently accessed loupe data in your own contracts if performance becomes a bottleneck, though direct calls are generally gas-efficient. + + +## Security Considerations + + +The `DiamondLoupeFacet` is a read-only facet. Its functions do not modify state and are generally considered safe. Ensure that the diamond's storage (specifically `s.selectors` and `s.facetAndPosition`) is managed securely by authorized facets, as the loupe facet's output is directly dependent on this underlying state. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx new file mode 100644 index 00000000..60e3cb4f --- /dev/null +++ b/website/docs/contracts/facets/ERC1155Facet.mdx @@ -0,0 +1,671 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements the ERC-1155 standard for fungible and non-fungible tokens. +- Supports batched operations for transfers and balance checks, improving efficiency. +- Provides flexible URI resolution for token metadata. + + +## Overview + +The ERC1155Facet provides a standard implementation for the ERC-1155 Multi-Token Standard within a Compose diamond. It manages token balances, approvals, and URI resolution, enabling the diamond to act as a versatile token issuer and manager. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `&#123;id&#125;` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; +import {ERC1155Facet} from "@compose/contracts/src/facets/ERC1155/ERC1155Facet.sol"; + +contract ERC1155DiamondConsumer { + address public diamondAddress; + + // Assume diamondAddress is set during deployment + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getTokenBalance(uint256 _id, address _account) external view returns (uint256) { + // Get the ERC1155Facet interface + IERC1155Facet erc1155Facet = IERC1155Facet(diamondAddress); + + // Call the balanceOf function on the diamond proxy + return erc1155Facet.balanceOf(_account, _id); + } + + function getTokenURI(uint256 _id) external view returns (string memory) { + IERC1155Facet erc1155Facet = IERC1155Facet(diamondAddress); + return erc1155Facet.uri(_id); + } +}`} + + +## Best Practices + + +- Initialize the ERC1155Facet with a dedicated storage slot during diamond deployment. +- Ensure appropriate access control is implemented at the diamond level for functions like `setApprovalForAll` if restricted operation is desired. +- When implementing custom token URIs, carefully manage the `baseURI` and `tokenURIs` storage to ensure correct resolution. + + +## Security Considerations + + +Access control for `setApprovalForAll` should be considered; by default, it's permissionless. Input validation for token IDs and amounts is handled by the facet, but downstream logic should also validate. Reentrancy is not a direct concern for most ERC1155 operations, but custom logic interacting with token transfers should be audited. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..b2f5c9ec --- /dev/null +++ b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx @@ -0,0 +1,417 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Enables secure cross-chain minting and burning of ERC20 tokens. +- Enforces `trusted-bridge` role for critical cross-chain operations. +- Provides internal mechanisms to access facet-specific storage directly. + + +## Overview + +The ERC20BridgeableFacet manages cross-chain minting and burning operations for ERC20 tokens within a Compose diamond. It enforces access control for trusted bridge operators and provides internal utility functions for interacting with diamond storage and verifying bridge permissions. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "@compose-protocol/diamond-contracts/contracts/facets/erc20/ERC20BridgeableFacet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; + +contract Deployer { + function deploy() public { + // Assume diamondDeployer is an instance of a contract that handles diamond deployment + // and has already deployed the diamond contract and its facets. + // The ERC20BridgeableFacet is assumed to be cut into the diamond at a specific address. + address diamondAddress = address(0xYourDiamondAddress); + IERC20BridgeableFacet erc20BridgeableFacet = IERC20BridgeableFacet(diamondAddress); + + // Example of cross-chain minting (requires trusted-bridge role) + // address to = address(0xRecipientAddress); + // uint256 amount = 1000 ether; + // address tokenAddress = address(0xERC20TokenAddress); + // erc20BridgeableFacet.crosschainMint(to, amount, tokenAddress); + + // Example of cross-chain burning (requires trusted-bridge role) + // erc20BridgeableFacet.crosschainBurn(address(0xSenderAddress), 500 ether, address(0xERC20TokenAddress)); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is correctly assigned within the diamond's AccessControl facet for authorized cross-chain operations. +- Utilize the `getERC20Storage` and `getAccessControlStorage` functions for internal logic that requires direct access to facet storage, ensuring correct slot referencing via assembly. +- When upgrading, ensure the storage layout compatibility is maintained if new state variables are introduced in future versions of this facet. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are protected by an access control check that requires the caller to possess the `trusted-bridge` role. The `checkTokenBridge` internal function explicitly validates this role and prevents zero-address callers. There is no explicit reentrancy guard; callers must ensure that the operations triggered by minting or burning do not introduce reentrancy vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx new file mode 100644 index 00000000..fce59584 --- /dev/null +++ b/website/docs/contracts/facets/ERC20BurnFacet.mdx @@ -0,0 +1,276 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Contract documentation for ERC20BurnFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ERC20BurnFacet + + + +- Allows for the reduction of total token supply by destroying tokens. +- Supports burning tokens directly from the caller's balance. +- Enables burning tokens from another account, contingent on an approved allowance. + + +## Overview + +The ERC20BurnFacet provides functionality to destroy ERC20 tokens within a Compose diamond. It enables users to burn their own tokens or burn tokens from other accounts if an allowance is set, facilitating token supply reduction and management. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-solidity/src/diamond/IDiamondCut.sol"; +import {IERC20BurnFacet} from "@compose/diamond-solidity/src/facets/erc20/IERC20BurnFacet.sol"; + +contract Deployer { + address diamondAddress; + + function deploy() public { + // ... deployment logic to create the diamond proxy ... + diamondAddress = address(0xYourDiamondProxyAddress); + + // Add ERC20BurnFacet + address erc20BurnFacetAddress = address(new ERC20BurnFacet()); + bytes32[] memory selectors = new bytes32[](3); + selectors[0] = IERC20BurnFacet.getStorage.selector; + selectors[1] = IERC20BurnFacet.burn.selector; + selectors[2] = IERC20BurnFacet.burnFrom.selector; + + bytes[] memory functionSignatures = new bytes[](3); + functionSignatures[0] = abi.encodeWithSignature("getStorage() returns (ERC20Storage storage)"); + functionSignatures[1] = abi.encodeWithSignature("burn(uint256 _amount)"); + functionSignatures[2] = abi.encodeWithSignature("burnFrom(address _from, uint256 _amount)"); + + IDiamondCut(diamondAddress).diamondCut( + IDiamondCut.FacetCut[] + ( + { + facetAddress: erc20BurnFacetAddress, + action: IDiamondCut.Action.ADD, + functionSelectors: selectors + } + ), + address(0), + "" + ); + } + + function burnMyTokens() public { + IERC20BurnFacet(diamondAddress).burn(100 * 1 ether); + } + + function burnAllowanceTokens(address _spender, uint256 _amount) public { + IERC20BurnFacet(diamondAddress).burnFrom(_spender, _amount); + } +}`} + + +## Best Practices + + +- Initialize the ERC20BurnFacet within your diamond's deployment script, ensuring the correct selectors are mapped. +- When calling `burnFrom`, ensure an allowance has been previously set for the caller by the `_from` address. +- The `burn` and `burnFrom` functions correctly emit a `Transfer` event to the zero address, signaling token destruction. + + +## Security Considerations + + +The `burnFrom` function relies on the ERC20 `allowance` mechanism. Ensure that the `allowance` is managed securely and that users understand the implications of granting allowances. No reentrancy risks are present as the functions do not make external calls. Input validation for `_amount` should be handled by the caller or the ERC20 storage logic to prevent underflows or overflows. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx new file mode 100644 index 00000000..4d0d3ba0 --- /dev/null +++ b/website/docs/contracts/facets/ERC20Facet.mdx @@ -0,0 +1,594 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Contract documentation for ERC20Facet" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ERC20Facet + + + +- Full ERC20 token functionality including name, symbol, decimals, supply, balances, and allowances. +- Supports standard token transfer and approval operations. +- Integrates seamlessly with the Compose diamond proxy pattern, allowing it to be composed with other facets. + + +## Overview + +The ERC20Facet implements the ERC20 token standard on a Compose diamond. It provides standard functions for managing token supply, balances, allowances, and facilitating token transfers. This facet enables a diamond to act as a compliant ERC20 token, allowing for fungible asset management within the diamond's ecosystem. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Facet} from "@compose/diamond/facets/ERC20/IERC20Facet.sol"; +import {IDiamondLoupe} from "@compose/diamond/facets/DiamondLoupe/IDiamondLoupe.sol"; + +contract ERC20Consumer { + IERC20Facet private erc20Facet; + + function initialize(address _diamondAddress) external { + // Assume the diamond is already deployed and has the ERC20Facet functions + // attached to it. This is typically done during diamond deployment. + erc20Facet = IERC20Facet(_diamondAddress); + } + + function getTokenName() external view returns (string memory) { + return erc20Facet.name(); + } + + function getTokenSymbol() external view returns (string memory) { + return erc20Facet.symbol(); + } + + function getTokenDecimals() external view returns (uint8) { + return erc20Facet.decimals(); + } + + function getTotalSupply() external view returns (uint256) { + return erc20Facet.totalSupply(); + } + + function getBalanceOf(address _account) external view returns (uint256) { + return erc20Facet.balanceOf(_account); + } + + function getAllowance(address _owner, address _spender) external view returns (uint256) { + return erc20Facet.allowance(_owner, _spender); + } + + function approve(address _spender, uint256 _amount) external { + erc20Facet.approve(_spender, _amount); + } + + function transfer(address _recipient, uint256 _amount) external { + erc20Facet.transfer(_recipient, _amount); + } + + function transferFrom(address _sender, address _recipient, uint256 _amount) external { + erc20Facet.transferFrom(_sender, _recipient, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20Facet is correctly initialized with the diamond's storage layout during deployment. +- Implement access control mechanisms for functions like `approve`, `transfer`, and `transferFrom` if specific roles or permissions are required beyond standard ERC20 behavior. +- When upgrading, ensure the storage slot for ERC20 state remains consistent to prevent data loss or corruption. + + +## Security Considerations + + +Standard ERC20 reentrancy considerations apply to `transfer` and `transferFrom`. Ensure that any custom logic interacting with these functions or emitted events is reentrancy-safe. Input validation for amounts and addresses should be handled by the facet itself to prevent unexpected behavior. Access to `approve` and `transferFrom` should be managed carefully if specific permissioning is layered on top of the standard ERC20 logic. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx new file mode 100644 index 00000000..535b8fc5 --- /dev/null +++ b/website/docs/contracts/facets/ERC20PermitFacet.mdx @@ -0,0 +1,337 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "Contract documentation for ERC20PermitFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ERC20PermitFacet + + + +- Implements EIP-2612 permit functionality for gas-efficient token approvals. +- Provides `nonces` to track and prevent replay attacks for permits. +- Exposes `DOMAIN_SEPARATOR` for correct signature verification against the specific diamond instance and chain ID. + + +## Overview + +The ERC20PermitFacet enables EIP-2612 compliant ERC20 token approvals via off-chain signatures. It integrates with the diamond proxy to manage token permits, allowing users to grant allowances without direct on-chain transactions for each approval. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +--- +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IDiamondCut} from "../diamond/IDiamond.sol"; +import {ERC20PermitFacet} from "./ERC20PermitFacet.sol"; + +contract Deployer { + function deploy() external { + // Assume diamond is deployed and facets are registered + address diamond = address(0x123); // Replace with actual diamond address + + ERC20PermitFacet permitFacet = new ERC20PermitFacet(); + + IDiamondCut(diamond).diamondCut( + new IDiamondCut.FacetCut[]( + {facetAddress: address(permitFacet), action: IDiamondCut.FacetCutAction.ADD, selectors: IDiamondCut.getSelectors(permitFacet)} + ), + address(0), + "" + ); + + // To use permit: + // 1. Get nonce: ERC20PermitFacet(diamond).nonces(owner) + // 2. Get domain separator: ERC20PermitFacet(diamond).DOMAIN_SEPARATOR() + // 3. Construct permit data and sign off-chain + // 4. Call permit: + // ERC20PermitFacet(diamond).permit(owner, spender, value, deadline, v, r, s); + } +}`} + + +## Best Practices + + +- Initialize the facet during diamond deployment or upgrade, ensuring the `DOMAIN_SEPARATOR` is correctly configured for the deployed chain. +- Use the `nonces` and `DOMAIN_SEPARATOR` functions to construct valid permit data off-chain before calling the `permit` function. +- Ensure the owner of the token correctly signs the permit message to prevent unauthorized approvals. + + +## Security Considerations + + +The `permit` function relies on off-chain signatures. Ensure the signature verification process is robust and that the `owner` address signing the permit has sufficient balance and is the intended grantor of the allowance. The `nonces` mapping prevents replay attacks; it's crucial that this mapping is managed correctly and incremented upon successful permit usage. The `DOMAIN_SEPARATOR` ensures that signatures are chain-specific, preventing cross-chain replay attacks. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx new file mode 100644 index 00000000..12b3c978 --- /dev/null +++ b/website/docs/contracts/facets/ERC6909Facet.mdx @@ -0,0 +1,529 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements the ERC-6909 standard for flexible token management. +- Supports both fungible and non-fungible token semantics through token IDs. +- Provides explicit functions for balance checks, allowances, and operator approvals. +- Enables direct token transfers via `transfer` and `transferFrom`. + + +## Overview + +The ERC6909Facet implements the ERC-6909 standard for fungible and non-fungible tokens within a Compose diamond. It provides core functionalities for managing token balances, allowances, operator approvals, and executing transfers, acting as a primary interface for token interactions. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Facet} from "@compose/contracts/interfaces/token/ERC6909/IERC6909Facet.sol"; +import {IERC6909Storage} from "@compose/contracts/interfaces/token/ERC6909/IERC6909Storage.sol"; + +contract ERC6909Consumer { + // Assume diamondAbi is the ABI of your diamond proxy + // Assume diamondAddress is the address of your diamond proxy + IERC6909Facet public erc6909Facet; + + constructor(address diamondAddress) { + erc6909Facet = IERC6909Facet(diamondAddress); + } + + function consumeERC6909() external { + // Example: Get balance + uint256 balance = erc6909Facet.balanceOf(address(this), 1); // For token ID 1 + + // Example: Approve allowance + erc6909Facet.approve(msg.sender, 1, 100); // Approve 100 units of token ID 1 + + // Example: Transfer tokens + erc6909Facet.transfer(msg.sender, address(this), 1, 50); // Transfer 50 units of token ID 1 + } +}`} + + +## Best Practices + + +- Initialize the ERC6909 storage correctly during diamond deployment or upgrade. The `getStorage` function provides access to the storage slot. +- Carefully manage access control for functions like `setOperator` and `approve` to prevent unauthorized actions. +- Ensure token IDs and amounts are validated before calling transfer or approve functions. + + +## Security Considerations + + +The `transfer` and `transferFrom` functions should be carefully audited for reentrancy risks if they interact with external contracts. Ensure proper input validation for token IDs, amounts, sender, and receiver addresses to prevent unexpected behavior or exploits. Access control for `approve` and `setOperator` is critical to prevent unauthorized token management. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx new file mode 100644 index 00000000..3f7a0a06 --- /dev/null +++ b/website/docs/contracts/facets/ERC721BurnFacet.mdx @@ -0,0 +1,221 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Enables the irreversible destruction of ERC-721 tokens. +- Integrates seamlessly with the Compose diamond storage pattern. +- Provides a clear interface for token burning operations. + + +## Overview + +The ERC721BurnFacet provides the functionality to burn (destroy) ERC-721 tokens within a Compose diamond. It allows for the removal of tokens from tracking and enumeration, reducing the total supply and freeing up associated metadata. This facet interacts directly with the diamond's storage to manage token states. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "@compose/contracts/src/facets/erc721/ERC721BurnFacet.sol"; +import {IDiamondCut} from "@compose/contracts/src/interfaces/IDiamondCut.sol"; + +contract Deployer { + address constant ERC721_BURN_FACET_ADDRESS = address(0xABC); // Replace with actual facet address + address constant DIAMOND_ADDRESS = address(0xDEF); // Replace with actual diamond address + + function deploy() external { + IDiamondCut(DIAMOND_ADDRESS).diamondCut( + IDiamondCut.FacetCut[]( + (IDiamondCut.FacetCut({ + facetAddress: ERC721_BURN_FACET_ADDRESS, + action: IDiamondCut.FacetCutAction.ADD, + selectors: + bytes4(keccak256("burn(uint256)")) | + bytes4(keccak256("getStorage()\0x1a2b3c4d")) // Example selector, replace with actual + })) + ), + address(0), // Initialize facetAddresses (optional) + bytes("") // Initialize facetCalldata (optional) + ); + } + + function burnToken(uint256 tokenId) external { + IERC721BurnFacet(DIAMOND_ADDRESS).burn(tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721BurnFacet` is added to the diamond with the correct selectors during deployment or upgrade. +- Implement proper access control mechanisms within your diamond's governance to restrict who can call the `burn` function, if necessary. +- Understand that burning a token is irreversible; ensure user intent is validated before execution. + + +## Security Considerations + + +The `burn` function should be protected by appropriate access control to prevent unauthorized token destruction. The facet directly manipulates internal storage, so ensure its integration does not conflict with other facets managing token states or ownership. Reentrancy is not a concern as the function does not make external calls. Input validation on `tokenId` is crucial to prevent unexpected behavior. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..16d0b86f --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,224 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Enables burning of ERC721 tokens, effectively destroying them. +- Integrates with enumeration tracking to maintain accurate token lists after burning. + + +## Overview + +The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens while ensuring proper removal from enumeration tracking. It exposes a method to retrieve its internal storage and a dedicated burn function. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurnFacet} from "@compose/contracts/src/facets/ERC721/ERC721EnumerableBurnFacet.sol"; + +contract ERC721EnumerableBurnFacetConsumer { + IERC721EnumerableBurnFacet public immutable erc721EnumerableBurnFacet; + + constructor(address _diamondAddress) { + erc721EnumerableBurnFacet = IERC721EnumerableBurnFacet(_diamondAddress); + } + + function burnToken(uint256 _tokenId) public { + erc721EnumerableBurnFacet.burn(_tokenId); + } + + function getFacetStorage() public view returns (IERC721EnumerableBurnFacet.ERC721EnumerableBurnStorage memory) { + return erc721EnumerableBurnFacet.getStorage(); + } +}`} + + +## Best Practices + + +- Ensure the facet is properly initialized within the diamond proxy contract. +- Grant appropriate access control to the `burn` function if it's not intended to be permissionless. + + +## Security Considerations + + +The `burn` function should be protected by appropriate access control mechanisms to prevent unauthorized token destruction. Ensure that the caller has the necessary permissions to burn the specified token. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..7ea55781 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx @@ -0,0 +1,743 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements the full ERC721 standard, including enumerable extensions for tracking token counts and ownership by index. +- Provides essential NFT metadata functions: `name()`, `symbol()`, and `tokenURI()`. +- Supports approval mechanisms for single tokens (`approve`) and bulk approvals (`setApprovalForAll`). + + +## Overview + +The ERC721EnumerableFacet implements the ERC721 standard with enumerable extensions, providing core NFT functionalities like name, symbol, token URI, and ownership management. It also includes essential functions for tracking total supply, balance, owner of specific tokens, and approvals, facilitating comprehensive NFT operations within a Compose diamond. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableFacet} from \"@compose-protocol/diamond/contracts/facets/ERC721/IERC721EnumerableFacet.sol\"; + +contract ERC721EnumerableConsumer { + IERC721EnumerableFacet private immutable erc721Facet; + + constructor(address _diamondAddress) { + erc721Facet = IERC721EnumerableFacet(_diamondAddress); + } + + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); + } + + function getTokenSymbol() external view returns (string memory) { + return erc721Facet.symbol(); + } + + function getTotalSupply() external view returns (uint256) { + return erc721Facet.totalSupply(); + } + + function getBalance(address _owner) external view returns (uint256) { + return erc721Facet.balanceOf(_owner); + } + + function getOwnerOf(uint256 _tokenId) external view returns (address) { + return erc721Facet.ownerOf(_tokenId); + } +}`} + + +## Best Practices + + +- Initialize the ERC721EnumerableFacet within the diamond's deployment process, ensuring all required storage is correctly set up. +- When transferring tokens, prefer `safeTransferFrom` over `transferFrom` to ensure receiver compatibility if the receiver is a contract. +- Access control for functions like `approve` and `setApprovalForAll` is handled implicitly by the ERC721 standard; ensure correct ownership checks are performed by the caller. + + +## Security Considerations + + +The `safeTransferFrom` functions include checks to prevent reentrancy and ensure the receiving contract can handle ERC721 tokens. Standard ERC721 ownership checks are inherent to functions like `transferFrom`, `approve`, and `ownerOf`. Users must ensure they are interacting with the correct diamond address and that the caller has the necessary permissions for state-modifying functions. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx new file mode 100644 index 00000000..74f59fee --- /dev/null +++ b/website/docs/contracts/facets/ERC721Facet.mdx @@ -0,0 +1,669 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Full ERC-721 compliance. +- Support for token metadata via `tokenURI`. +- Internal transfer logic for robust state management. +- Approval mechanisms for token transfers. + + +## Overview + +The ERC721Facet provides a full implementation of the ERC-721 Non-Fungible Token Standard within a Compose diamond. It handles token creation, transfers, approvals, and metadata retrieval, acting as the primary interface for ERC-721 compliant collections. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose/contracts/interfaces/IERC721Facet.sol"; +import {DiamondProxy} from "@compose/contracts/DiamondProxy.sol"; + +contract ERC721Deployer { + IERC721Facet public erc721Facet; + + function deployERC721(address diamondProxyAddress) external { + erc721Facet = IERC721Facet(diamondProxyAddress); + // Assume ERC721Facet has been added to the diamond. + } + + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); + } + + function getTokenSymbol() external view returns (string memory) { + return erc721Facet.symbol(); + } + + function getTokenOwner(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); + } + + function transferToken(address to, uint256 tokenId) external { + // Ensure caller is approved or owner + erc721Facet.transferFrom(msg.sender, to, tokenId); + } +}`} + + +## Best Practices + + +- Initialize the ERC721Facet with a unique name and symbol during diamond deployment. +- Utilize `safeTransferFrom` for transfers to ensure receiver contract compatibility. +- Manage approvals carefully, especially for `setApprovalForAll`, to prevent unintended asset access. + + +## Security Considerations + + +The `transferFrom` and `safeTransferFrom` functions require careful access control to ensure only the token owner or an approved address can initiate a transfer. The `setApprovalForAll` function should be used with caution as it grants broad permissions. Reentrancy is mitigated by using Checks-Effects-Interactions pattern within transfer functions. Input validation is performed on token IDs and addresses. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx new file mode 100644 index 00000000..0199a7b8 --- /dev/null +++ b/website/docs/contracts/facets/ExampleDiamond.mdx @@ -0,0 +1,146 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Contract documentation for ExampleDiamond" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ExampleDiamond + + + +- Manages function selector to facet address mapping for routing. +- Initializes the diamond with an owner and an initial set of facets. +- Supports adding, replacing, and removing facets via its constructor's `FacetCut` structure. + + +## Overview + +The ExampleDiamond contract serves as the core of a Compose diamond proxy. It orchestrates facet registration and routing, acting as the primary interface for interacting with various diamond modules. Its constructor is crucial for initial setup, mapping function selectors to facet addresses. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut &#123; address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; &#125; Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ExampleDiamond} from "./ExampleDiamond.sol"; +import {SomeFacet} from "./SomeFacet.sol"; + +contract DeployDiamond { + // Define facet cuts + struct FacetCut { + address facetAddress; + uint8 action; // Add=0, Replace=1, Remove=2 + bytes4[] functionSelectors; + } + + function deploy() public { + // Example facet data + address someFacetAddress = address(new SomeFacet()); + bytes4[] memory someFacetSelectors = new bytes4[](1); + someFacetSelectors[0] = SomeFacet.someFunction.selector; // Assuming SomeFacet has someFunction + + FacetCut[] memory cuts = new FacetCut[](1); + cuts[0] = FacetCut({ + facetAddress: someFacetAddress, + action: 0, // Add + functionSelectors: someFacetSelectors + }); + + // Deploy ExampleDiamond and initialize + ExampleDiamond diamond = new ExampleDiamond(); + // The constructor of ExampleDiamond is called implicitly here, + // but for explicit initialization of facets, a separate function + // like \`diamondCut\` would typically be called after deployment. + // The provided ExampleDiamond's constructor directly takes facet data. + + // For demonstration, assume the constructor takes these cuts directly: + // ExampleDiamond diamond = new ExampleDiamond(cuts, msg.sender); + + // In a real scenario, you would call \`diamondCut\` on the deployed diamond proxy. + // diamond.diamondCut(cuts, address(0), ""); + } +}`} + + +## Best Practices + + +- Initialize the diamond with all necessary facets and their function selectors during deployment using the constructor. +- Ensure the owner is set correctly during initialization for future upgradeability. +- Understand that the `constructor` directly registers facets; subsequent upgrades would use a `diamondCut` function (not shown in the provided contract). + + +## Security Considerations + + +The `constructor` is a critical setup function. Ensure the `facetAddress` values provided are verified and the `functionSelectors` accurately reflect the functions intended to be exposed by each facet. Ownership transfer should be handled carefully after initialization to maintain control. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx new file mode 100644 index 00000000..7809a9b0 --- /dev/null +++ b/website/docs/contracts/facets/OwnerFacet.mdx @@ -0,0 +1,212 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Manages the single owner address for the diamond contract. +- Supports transferring ownership to a new address. +- Allows for complete renunciation of ownership. + + +## Overview + +The OwnerFacet provides essential ownership management for a Compose diamond. It allows for retrieving the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative actions and ensuring secure contract governance. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose-protocol/diamond/contracts/facets/Owner/IOwnerFacet.sol"; + +contract OwnerConsumer { + IOwnerFacet private immutable _ownerFacet; + + constructor(address _diamondAddress) { + _ownerFacet = IOwnerFacet(_diamondAddress); + } + + function getDiamondOwner() external view returns (address) { + return _ownerFacet.owner(); + } + + function transferDiamondOwnership(address _newOwner) external { + _ownerFacet.transferOwnership(_newOwner); + } + + function renounceDiamondOwnership() external { + _ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize the diamond with the OwnerFacet to establish initial ownership. +- Use `transferOwnership` for controlled transitions of administrative control. +- Ensure the `owner` address has appropriate permissions for critical diamond functions. + + +## Security Considerations + + +The `transferOwnership` function can be used to set the new owner to `address(0)`, effectively renouncing ownership. This action is irreversible. Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..e995de62 --- /dev/null +++ b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx @@ -0,0 +1,287 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Secure two-step ownership transfer mechanism. +- Explicit `acceptOwnership` call prevents accidental ownership changes. +- Functions `owner()`, `pendingOwner()`, `transferOwnership()`, `acceptOwnership()`, and `renounceOwnership()` provide full ownership management capabilities. + + +## Overview + +The OwnerTwoStepsFacet manages contract ownership through a two-step verification process. It allows the current owner to initiate a transfer to a new owner, who must then explicitly accept the ownership, enhancing security against accidental or malicious ownership changes. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +--- +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "../interfaces/IOwnerTwoStepsFacet.sol"; + +contract OwnerDeployer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function transferOwnershipToNewOwner(address _newOwner) public { + IOwnerTwoStepsFacet(diamondAddress).transferOwnership(_newOwner); + } + + function acceptOwnershipFromCurrentOwner() public { + IOwnerTwoStepsFacet(diamondAddress).acceptOwnership(); + } + + function renounceContractOwnership() public { + IOwnerTwoStepsFacet(diamondAddress).renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize ownership by calling `transferOwnership` with the desired address, followed by the new owner calling `acceptOwnership`. +- Use `owner()` and `pendingOwner()` to track the current and awaiting owner. +- Store `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` constants within your diamond deployment scripts or configuration for consistent slot access. + + +## Security Considerations + + +Ownership transfer functions (`transferOwnership`, `acceptOwnership`, `renounceOwnership`) are typically protected by access control mechanisms within the diamond's governance framework. Ensure that only authorized entities can call these functions. `transferOwnership` sets a pending owner, which is only updated to the actual owner upon a successful `acceptOwnership` call by the pending owner. Direct access to storage slots via `getOwnerStorage` and `getPendingOwnerStorage` requires careful handling to avoid unintended state modifications. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx new file mode 100644 index 00000000..6f9a48ff --- /dev/null +++ b/website/docs/contracts/facets/RoyaltyFacet.mdx @@ -0,0 +1,211 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements the `royaltyInfo` function as per ERC-2981. +- Supports dynamic configuration of token-specific royalties on top of a default royalty rate. +- Utilizes inline assembly for efficient access to storage, minimizing gas costs. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard for on-chain royalty payments. It allows for querying royalty information for a given token and sale price, supporting both token-specific and default royalty configurations. This facet centralizes royalty logic within the diamond, providing a consistent interface for marketplaces and other integrators. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; +import {IRoyaltyFacet} from "@compose/diamond/contracts/facets/RoyaltyFacet/IRoyaltyFacet.sol"; + +contract Deployer { + address public diamondAddress; + + function deployDiamond() public { + // Assume diamond implementation and facets are already deployed + // ... + address royaltyFacetAddress = address(new RoyaltyFacet()); // Example address + + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: royaltyFacetAddress, + action: IDiamondCut.FacetCutAction.ADD, + selectors: + new bytes4[](3) // Including getStorage, royaltyInfo, and any internal selectors if exposed + }); + // Set selectors for royaltyInfo and getStorage + cuts[0].selectors[0] = IRoyaltyFacet.royaltyInfo.selector; + cuts[0].selectors[1] = IRoyaltyFacet.getStorage.selector; + + // Assume diamond init contract is deployed and has the diamondCut function + // DiamondInit(diamondInitAddress).diamondCut(cuts, address(0), ""); + // diamondAddress = address(this); // Or wherever the diamond is deployed + } + + function getRoyaltyInfo(address _diamondAddress, uint256 _tokenId, uint256 _salePrice) public view returns (address, uint256) { + // Call royaltyInfo via the diamond proxy + // The diamond proxy will route the call to the RoyaltyFacet + bytes4 selector = IRoyaltyFacet.royaltyInfo.selector; + (bool success, bytes memory data) = _diamondAddress.call(abi.encodeWithSelector(selector, _tokenId, _salePrice)); + require(success, "Royalty query failed"); + return (abi.decode(data, (address, uint256))); + } +}`} + + +## Best Practices + + +- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. +- Ensure marketplaces or other integrators call the `royaltyInfo` function through the diamond proxy address. +- Store the `STORAGE_POSITION` for `RoyaltyStorage` in a well-known, immutable location accessible to all facets that might interact with royalty data. + + +## Security Considerations + + +The `royaltyInfo` function calculates royalties as a percentage of the sale price. Ensure that the basis points for royalties are validated to prevent excessive or zero royalty amounts. Access control is managed by the diamond proxy; ensure that only authorized entities can set or modify default/token-specific royalties if such administrative functions are implemented in other facets. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx new file mode 100644 index 00000000..af06c461 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlMod.mdx @@ -0,0 +1,447 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Emitted when the admin role for a role is changed." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Emitted when the admin role for a role is changed. + + + +- Role-based access control: Define and manage distinct roles with granular permissions. +- Permission management functions: `grantRole`, `revokeRole`, and `setRoleAdmin` provide flexible control over role assignments and administration. +- Explicit role checking: `hasRole` and `requireRole` allow for clear and auditable permission checks within facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlMod module provides a robust role-based access control system for Compose diamonds. It enables granular permission management by allowing roles to be granted, revoked, and checked, ensuring that only authorized accounts can perform sensitive operations. This is crucial for maintaining the security and integrity of diamond applications. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. **Returns** + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. **Parameters** **Returns** + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. **Parameters** **Returns** + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. **Note:** error: AccessControlUnauthorizedAccount If the account does not have the role. **Parameters** + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. **Parameters** **Returns** + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. **Parameters** + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. **Parameters** +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. **Parameters** +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. **Parameters** +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. **Parameters** +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose/diamond-contracts/contracts/modules/access/IAccessControlMod.sol"; + +contract AccessControlUserFacet { + IAccessControl internal accessControl; + + // Assume accessControl is initialized in an initializer function + // and points to the AccessControlMod facet address. + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + + function grantMinterRole(address _account) external { + // Ensure the caller has the authority to grant roles + accessControl.requireRole(_account, ADMIN_ROLE); + accessControl.grantRole(MINTER_ROLE, _account); + } + + function hasMinterRole(address _account) external view returns (bool) { + return accessControl.hasRole(MINTER_ROLE, _account); + } +}`} + + +## Best Practices + + +- Always use `requireRole` for internal checks within facets to enforce access control before executing critical logic. +- Manage role administration carefully. Only grant the `ADMIN_ROLE` to trusted accounts, and use `setRoleAdmin` to define clear administrative hierarchies for other roles. +- When upgrading the diamond, ensure the AccessControlMod facet is properly updated or re-initialized if its storage layout changes, to maintain consistent access control. + + +## Integration Notes + + +The AccessControlMod module manages its state within the diamond's storage. Facets interact with it through the `IAccessControl` interface. Changes to roles and role admins made via the AccessControlMod functions are immediately reflected and visible to all facets interacting with the module. Ensure the `AccessControlMod` facet is deployed and its address is correctly registered in the diamond's facet registry for other facets to interact with it. The module relies on the diamond's storage pattern for state persistence. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx new file mode 100644 index 00000000..f62a69b6 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlPausableMod.mdx @@ -0,0 +1,384 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Event emitted when a role is paused." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Event emitted when a role is paused. + + + +- Granular role pausing: Allows individual roles to be paused independently. +- Role-specific checks: `requireRoleNotPaused` enforces both role ownership and active status. +- Access control storage access: Provides functions to retrieve underlying storage structures for inspection. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlPausable module provides role-based access control with the ability to pause specific roles. This enables granular control over sensitive operations, allowing administrators to temporarily halt functionality associated with certain roles without affecting the entire system. It enhances diamond safety by offering a mechanism to mitigate risks during emergencies or upgrades. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. **Returns** + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. **Returns** + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. **Parameters** **Returns** + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. **Parameters** + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRolePaused If the role is paused. **Parameters** + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. **Parameters** + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. **Parameters** +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. **Parameters** +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. **Parameters** +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. **Parameters** +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausable} from "@compose/modules/access-control/IAccessControlPausable.sol"; + +contract MyFacet { + IAccessControlPausable public constant accessControlPausable = IAccessControlPausable(address(this)); // Replace with actual diamond proxy address + + address public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + + /** + * @notice Pauses the ADMIN_ROLE. + */ + function pauseAdminRole() external { + accessControlPausable.pauseRole(ADMIN_ROLE); + } + + /** + * @notice Unpauses the ADMIN_ROLE. + */ + function unpauseAdminRole() external { + accessControlPausable.unpauseRole(ADMIN_ROLE); + } + + /** + * @notice Requires that the caller has the ADMIN_ROLE and that the role is not paused. + */ + function sensitiveOperation() external { + accessControlPausable.requireRoleNotPaused(msg.sender, ADMIN_ROLE); + // ... perform sensitive operation ... + } +}`} + + +## Best Practices + + +- Ensure that roles intended to be paused are properly managed and that only authorized entities can call `pauseRole` and `unpauseRole`. +- Utilize `requireRoleNotPaused` before executing critical functions to prevent unauthorized actions when a role is paused. +- Understand that pausing a role affects all accounts assigned to it; consider the implications before pausing. + + +## Integration Notes + + +This module interacts with the diamond's storage to manage role pausing states. Facets can call the public functions of this module directly via the diamond proxy. The `AccessControlPausable` storage is distinct from the base `AccessControl` storage. Changes to role pausing are immediately reflected across all facets interacting with the module. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..faa2d976 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlTemporalMod.mdx @@ -0,0 +1,484 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Event emitted when a role is granted with an expiry timestamp." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Event emitted when a role is granted with an expiry timestamp. + + + +- Time-bound role assignments with explicit expiry timestamps. +- Functions to check if a role has expired (`isRoleExpired`) or is currently valid (`requireValidRole`). +- Ability to grant roles with future expiry dates, facilitating scheduled access changes. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlTemporalMod provides time-bound role assignments within a Compose diamond. This module enables granting roles that automatically expire, enhancing security and access management by ensuring privileges are not permanent unless explicitly renewed. It integrates seamlessly with the diamond's storage pattern for robust, upgradeable access control. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. **Returns** + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. **Parameters** **Returns** + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. **Returns** + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. **Parameters** **Returns** + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. **Parameters** **Returns** + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRoleExpired If the role has expired. **Parameters** + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. **Parameters** **Returns** + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. **Parameters** +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. **Parameters** +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. **Parameters** +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. **Parameters** +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/AccessControlTemporalMod.sol"; + +contract MyFacet { + address constant ACCESS_CONTROL_TEMPORAL_MODULE = address(0x123); // Replace with actual module address + + IAccessControlTemporalMod accessControlTemporalMod = IAccessControlTemporalMod(ACCESS_CONTROL_TEMPORAL_MODULE); + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + + /** + * @notice Grants an admin role to an account with a specific expiry. + * @param _account The account to grant the role to. + * @param _expiry The timestamp when the role expires. + */ + function grantAdminRoleWithExpiry(address _account, uint64 _expiry) external { + accessControlTemporalMod.grantRoleWithExpiry(ADMIN_ROLE, _account, _expiry); + } + + /** + * @notice Checks if a user has a valid, non-expired admin role. + * @param _account The account to check. + */ + function checkAdminRole(address _account) external { + accessControlTemporalMod.requireValidRole(ADMIN_ROLE, _account); + } +}`} + + +## Best Practices + + +- Use `grantRoleWithExpiry` to assign time-limited permissions, ensuring roles do not persist indefinitely. +- Implement checks using `requireValidRole` to enforce current, non-expired role access before critical operations. +- Design role expiration timestamps carefully, considering operational needs and security implications to avoid unintended access revocations or lingering privileges. + + +## Integration Notes + + +AccessControlTemporalMod stores its state within the diamond's storage. Facets interact with this module via its interface. The `grantRoleWithExpiry`, `revokeTemporalRole`, `getRoleExpiry`, and `isRoleExpired` functions operate on the module's dedicated storage slots, which are managed by the diamond proxy. Changes to role assignments and their expiry are immediately visible to all facets through the module's interface. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx new file mode 100644 index 00000000..e60e8131 --- /dev/null +++ b/website/docs/contracts/modules/DiamondCutMod.mdx @@ -0,0 +1,385 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Documentation for DiamondCutMod" +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Documentation for DiamondCutMod + + + +- Allows dynamic addition, replacement, and removal of facets and their functions. +- Supports executing an arbitrary function via `delegatecall` immediately after a diamond cut operation, enabling complex initialization or state changes. +- Provides a `getStorage` function for inspecting facet storage, aiding in debugging and understanding diamond state. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCutMod provides the core functionality for managing facets within a Compose diamond. It enables dynamic addition, replacement, and removal of functions, ensuring the diamond's capabilities can be upgraded and extended safely. This module is crucial for maintaining the diamond's composability and adaptability. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +**Note:** storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall **Parameters** + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-proxy/contracts/interfaces/IDiamondCut.sol"; + +contract MyFacet { + IDiamondCut public diamondCut = IDiamondCut(address(this)); // Assuming diamondCut is accessible + + /** + * @notice Adds new functions to the diamond. + * @param _facetAddress The address of the facet contract. + * @param _functionSelectors An array of function selectors to add. + */ + function addMyFunctions(address _facetAddress, bytes4[] memory _functionSelectors) external { + // Example of adding functions to the diamond + diamondCut.diamondCut( + DiamondCutMod.FacetCut[] + (new DiamondCutMod.FacetCut[](1)) + , + address(0), // Not replacing + bytes('') // Not executing + ); + + // Note: In a real scenario, you would construct the FacetCut struct properly. + // The above is a simplified representation for demonstration. + } + + // Other functions in MyFacet... +}`} + + +## Best Practices + + +- Ensure all calls to `diamondCut` are properly authorized to prevent unauthorized modifications to the diamond's function mappings. +- Carefully validate the `facetAddress` and `functionSelectors` passed to `addFunctions`, `removeFunctions`, and `replaceFunctions` to avoid introducing malicious or unintended logic. +- Use custom errors for revert reasons to provide clear, gas-efficient feedback on cut operation failures. + + +## Integration Notes + + +The DiamondCutMod directly manipulates the diamond's internal storage that maps function selectors to facet addresses and their implementations. Any changes made via `diamondCut`, `addFunctions`, `removeFunctions`, or `replaceFunctions` are immediately reflected in the diamond proxy's routing logic. Facets should be aware that the implementation address for a given function selector can change, and they should always call functions through the diamond proxy's address to ensure they are routed to the correct, current implementation. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx new file mode 100644 index 00000000..25e6c634 --- /dev/null +++ b/website/docs/contracts/modules/DiamondMod.mdx @@ -0,0 +1,236 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Diamond Library - Internal functions and storage for diamond proxy functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond Library - Internal functions and storage for diamond proxy functionality. + + + +- Enables dynamic facet registration during initial deployment via `addFacets`. +- Provides a `diamondFallback` mechanism to route external calls to the appropriate facet. +- Exposes `getStorage` for retrieving the diamond's internal storage layout. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondMod library provides essential internal functions for managing diamond proxy facets and handling function calls. It is crucial for diamond deployment and runtime operation, enabling dynamic facet addition and robust fallback mechanisms for function execution. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +**Note:** storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondMod} from "@compose/diamond-proxy/contracts/DiamondMod.sol"; + +contract MyFacet { + DiamondMod internal diamondMod; + + constructor(address diamondModAddress) { + diamondMod = DiamondMod(diamondModAddress); + } + + /** + * @notice Example of calling getStorage via DiamondMod. + */ + function readDiamondStorage() external view returns (bytes memory) { + return diamondMod.getStorage(); + } +}`} + + +## Best Practices + + +- Use `addFacets` exclusively during diamond deployment to ensure deterministic facet registration. +- Implement custom errors or revert strings within facets to clearly indicate failures during `diamondFallback` execution. +- Ensure that `DiamondMod` is initialized correctly within the diamond proxy's deployment or upgrade process. + + +## Integration Notes + + +DiamondMod interacts directly with the diamond proxy's storage. The `addFacets` function modifies the mapping of function selectors to facet addresses, which is fundamental to the diamond's operation. `diamondFallback` relies on this mapping to dispatch calls. `getStorage` returns the raw storage data, which facets can interpret based on their own storage layouts. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx new file mode 100644 index 00000000..cd20d4d3 --- /dev/null +++ b/website/docs/contracts/modules/ERC1155Mod.mdx @@ -0,0 +1,624 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "ERC-1155 Token Receiver Interface - Handles the receipt of a single ERC-1155 token type." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-1155 Token Receiver Interface - Handles the receipt of a single ERC-1155 token type. + + + +- Implements standard ERC-1155 `safeTransferFrom` and `safeBatchTransferFrom` for secure token transfers. +- Supports minting and burning of single tokens and batches, including receiver validation for contract addresses. +- Provides functionality to set and manage base and token-specific URIs for metadata representation. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod facet provides a robust implementation for handling ERC-1155 token operations within a Compose diamond. It manages token minting, burning, transfers, and URI management, ensuring adherence to EIP-1155 standards. This module is crucial for any diamond requiring fungible and non-fungible token capabilities, offering a composable and upgradeable solution. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. **Note:** storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. **Parameters** + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. **Parameters** + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. **Returns** + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. **Parameters** + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. **Parameters** + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. **Parameters** + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. **Parameters** + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. **Parameters** + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. **Parameters** + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. **Parameters** +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. **Parameters** +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. **Parameters** +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. **Parameters** +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. **Parameters** +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. **Parameters** +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IDiamondCut} from "../diamond/interfaces/IDiamondCut.sol"; + +// Assume ERC1155ModFacet is deployed and its address is known +contract MyDiamondConsumer { + address internal diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + // Example function to mint ERC1155 tokens + function mintErc1155Tokens(address _to, uint256 _id, uint256 _amount, bytes memory _data) external { + // Call the ERC1155Mod facet via the diamond proxy + (bool success, ) = diamondAddress.call(abi.encodeWithSelector( + ERC1155Mod.mint.selector, + _to, + _id, + _amount, + _data + )); + require(success, "ERC1155Mod: mint call failed"); + } + + // Example function to transfer ERC1155 tokens + function transferErc1155Tokens(address _from, address _to, uint256 _id, uint256 _amount, bytes memory _data) external { + // Call the ERC1155Mod facet via the diamond proxy + (bool success, ) = diamondAddress.call(abi.encodeWithSelector( + ERC1155Mod.safeTransferFrom.selector, + _from, + _to, + _id, + _amount, + _data + )); + require(success, "ERC1155Mod: safeTransferFrom call failed"); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented at the diamond level for sensitive functions like minting and burning. +- Always validate receiver addresses, especially for contract recipients, to prevent unintended token loss or unexpected behavior. +- Be mindful of gas costs when performing batch operations (mintBatch, burnBatch, safeBatchTransferFrom) with large arrays. + + +## Integration Notes + + +The ERC1155Mod facet interacts with diamond storage to manage token balances, approvals, and URI configurations. Token balances are stored in mappings within the diamond's storage. The `getStorage` function provides direct access to the ERC1155 storage struct via inline assembly, allowing other facets to read the state. Any modifications to token balances or URIs by this facet are immediately reflected in the diamond's global state. Facets extending ERC1155 functionality should be aware of the storage layout and potential for collisions if adding new state. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx new file mode 100644 index 00000000..4268ea9e --- /dev/null +++ b/website/docs/contracts/modules/ERC165Mod.mdx @@ -0,0 +1,159 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. + + + +- Standard ERC-165 interface detection capabilities. +- Manages interface registration and querying via internal functions. +- Enables facets to declare their supported interfaces programmatically. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +ERC165Mod provides essential functionality for ERC-165 interface detection within a Compose diamond. It manages the registration and querying of supported interfaces, ensuring compliance with the ERC-165 standard. This module is critical for enabling diamond facets to correctly advertise their capabilities to external callers. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. **Returns** + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` **Parameters** + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC165Mod} from "@compose/modules/ERC165Mod.sol"; +import {ERC165Mod} from "@compose/modules/ERC165Mod.sol"; + +contract MyFacet { + // Assuming ERC165Mod is already initialized and its storage pointer is known. + // In a real scenario, you would get this from the Diamond contract. + address constant ERC165_MODULE_ADDRESS = address(0x1); // Placeholder + + function initialize() external { + // Example: Registering ERC721 interface during facet initialization + ERC165Mod.registerInterface(ERC165_MODULE_ADDRESS, type(IERC721).interfaceId); + } + + function supportsInterface(bytes4 interfaceId) external view returns (bool) { + // Example: Checking if an interface is supported + return ERC165Mod.supportsInterface(ERC165Mod.getStorage(ERC165_MODULE_ADDRESS), interfaceId); + } +}`} + + +## Best Practices + + +- Register all supported interfaces during facet initialization to ensure accurate reporting. +- Use the `supportsInterface` function provided by the module to check for interface support, rather than directly accessing storage. +- Ensure the ERC165Mod is correctly initialized and accessible within the diamond's upgrade process. + + +## Integration Notes + + +The ERC165Mod utilizes a specific storage slot for its `ERC165Storage` struct, as managed by the diamond proxy. Facets interact with this module by calling its external functions, which in turn use inline assembly to access and manipulate the `ERC165Storage` at its designated position. The `getStorage` function is crucial for obtaining a pointer to this storage, allowing other facets to interact with the ERC-165 state. It's imperative that the `ERC165Storage` struct definition and its slot remain consistent across upgrades. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..ba2884b1 --- /dev/null +++ b/website/docs/contracts/modules/ERC20BridgeableMod.mdx @@ -0,0 +1,439 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "LibERC20Bridgeable — ERC-7802 Library - Revert when a provided receiver is invalid(e.g,zero address) ." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Bridgeable — ERC-7802 Library - Revert when a provided receiver is invalid(e.g,zero address) . + + + +- Enforces `trusted-bridge` role for cross-chain burn and mint operations. +- Validates receiver addresses to prevent sending to zero address. +- Provides internal helper functions for access control checks. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20BridgeableMod provides essential functionality for managing cross-chain ERC20 token transfers within a Compose diamond. It enforces access control for trusted bridge operators and ensures the validity of receiver addresses, enhancing the security and reliability of inter-chain token operations. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. **Note:** storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. **Parameters** + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. **Parameters** + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. **Parameters** + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. **Returns** + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. **Parameters** +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. **Parameters** +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. **Parameters** +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. **Parameters** +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. **Parameters** +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . **Parameters** +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC20Bridgeable, IERC20BridgeableMod} from "@compose/modules/ERC20BridgeableMod.sol"; +import {DiamondStorage} from "@compose/core/DiamondStorage.sol"; + +contract ERC20BridgeableFacet { + using LibERC20Bridgeable for DiamondStorage; + + /** + * @notice Burns ERC20 tokens for a cross-chain operation. + * @param _token The address of the ERC20 token. + * @param _to The recipient address on the destination chain. + * @param _amount The amount of tokens to burn. + */ + function burnForCrosschain(address _token, address _to, uint256 _amount) external { + // Ensure the caller is a trusted bridge operator + LibERC20Bridgeable.checkTokenBridge(msg.sender); + + // Perform the cross-chain burn operation + LibERC20Bridgeable.crosschainBurn(_token, _to, _amount); + } + + /** + * @notice Mints ERC20 tokens for a cross-chain operation. + * @param _token The address of the ERC20 token. + * @param _to The recipient address on the destination chain. + * @param _amount The amount of tokens to mint. + */ + function mintForCrosschain(address _token, address _to, uint256 _amount) external { + // Ensure the caller is a trusted bridge operator + LibERC20Bridgeable.checkTokenBridge(msg.sender); + + // Perform the cross-chain mint operation + LibERC20Bridgeable.crosschainMint(_token, _to, _amount); + } +}`} + + +## Best Practices + + +- Only addresses with the `trusted-bridge` role should be allowed to call `crosschainBurn` and `crosschainMint` functions. +- Always validate the receiver address (`_to`) to prevent accidental token loss or sending to zero address. +- Ensure the `ERC20BridgeableMod` is correctly initialized with trusted bridge addresses. + + +## Integration Notes + + +The `ERC20BridgeableMod` relies on the `AccessControl` module for managing the `trusted-bridge` role. It also interacts with ERC20 token contracts directly. The `getAccessControlStorage` and `getERC20Storage` functions provide internal access to these respective storage layouts, ensuring that facets can correctly interact with the underlying storage slots managed by the diamond. Changes to the `trusted-bridge` role within `AccessControl` will directly impact the authorization of bridge operations. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx new file mode 100644 index 00000000..f2b7faf7 --- /dev/null +++ b/website/docs/contracts/modules/ERC20Mod.mdx @@ -0,0 +1,425 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "LibERC20 — ERC-20 Library - Thrown when a sender attempts to transfer or burn more tokens than their balance." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20 — ERC-20 Library - Thrown when a sender attempts to transfer or burn more tokens than their balance. + + + +- Implements standard ERC-20 functions: mint, burn, transfer, transferFrom, approve. +- Manages total supply and individual token balances. +- Supports allowance mechanism for delegated token transfers. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod module provides a standard implementation for ERC-20 token functionality within a Compose diamond. It enables core operations like minting, burning, transferring, and approving token allowances, ensuring consistent and auditable token management across diamond applications. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. **Parameters** + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. **Parameters** + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. **Returns** + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. **Parameters** + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. **Parameters** + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. **Parameters** + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. **Parameters** +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. **Parameters** +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. **Parameters** +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. **Parameters** +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC20} from "path/to/LibERC20.sol"; +import {IERC20Mod} from "path/to/IERC20Mod.sol"; + +contract MyERC20Facet { + address immutable _diamondAddress; + + constructor(address diamondAddress) { + _diamondAddress = diamondAddress; + } + + function mintTokens(address _to, uint256 _amount) external { + IERC20Mod(_diamondAddress).mint(_to, _amount); + } + + function transferTokens(address _to, uint256 _amount) external { + IERC20Mod(_diamondAddress).transfer(msg.sender, _to, _amount); + } + + function approveSpending(address _spender, uint256 _amount) external { + IERC20Mod(_diamondAddress).approve(msg.sender, _spender, _amount); + } +}`} + + +## Best Practices + + +- Always use custom errors for revert conditions, such as insufficient balance or allowance, for gas efficiency and clarity. +- Ensure that all external callers interact with the diamond proxy address, not directly with facet implementations. +- When upgrading facets, be aware of storage layout changes and ensure compatibility to prevent data loss or corruption. + + +## Integration Notes + + +The ERC20Mod relies on a specific storage slot for its ERC-20 state. The `getStorage` function provides access to this state via inline assembly, ensuring it's correctly bound to the diamond's storage. Facets interacting with ERC20Mod must use the diamond proxy address and call the functions exposed by the `IERC20Mod` interface. Any changes to the ERC-20 storage layout within the module must be carefully managed to maintain compatibility with existing facets and diamond upgrades. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx new file mode 100644 index 00000000..d63ddc8d --- /dev/null +++ b/website/docs/contracts/modules/ERC20PermitMod.mdx @@ -0,0 +1,279 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Thrown when a permit signature is invalid or expired." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Permit — Library for ERC-2612 Permit Logic - Thrown when a permit signature is invalid or expired. + + + +- Implements the ERC-2612 standard for off-chain signature-based token approvals. +- Utilizes a dedicated storage slot for permit-related data, ensuring composability and upgrade safety. +- Provides access to the `DOMAIN_SEPARATOR` for consistent signature generation across facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20PermitMod provides the necessary logic for implementing the ERC-2612 'permit' functionality within a Compose diamond. This allows users to grant allowances to other addresses via signed off-chain messages, enhancing user experience and reducing gas costs for frequent allowance updates. + +--- + +## Storage + +### ERC20PermitStorage + +**Note:** storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +**Note:** storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for &#123;permit&#125;. This value is unique to a contract and chain ID combination to prevent replay attacks. **Returns** + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. **Parameters** + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. **Parameters** +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. **Parameters** +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC20Permit} from "@compose-protocol/diamond-contracts/contracts/module/erc20/LibERC20Permit.sol"; +import {IERC20Permit} from "@compose-protocol/diamond-contracts/contracts/interface/IERC20Permit.sol"; + +contract MyERC20Facet { + using LibERC20Permit for LibERC20Permit.PermitStorage; + + function permit(IERC20Permit._Permit calldata _permit, bytes32 _signature) external { + LibERC20Permit.PermitStorage storage ps = LibERC20Permit.getPermitStorage(); + address owner = ps.permit(_permit, _signature); + // The permit function emits the Approval event. + // If this facet needs to emit an Approval event, it must be done here. + // For example: emit Approval(owner, _permit.spender, _permit.value); + } + + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return LibERC20Permit.DOMAIN_SEPARATOR(); + } +}`} + + +## Best Practices + + +- Ensure the `permit` function is called within a facet that correctly emits the `Approval` event as specified by ERC-20 standards, as `LibERC20Permit.permit` itself does not emit it. +- Always verify the `DOMAIN_SEPARATOR` is correctly configured and matches the chain ID and contract address when implementing off-chain permit signing. +- Treat signature validation as a critical security step; ensure all parameters for `LibERC20Permit.permit` are accurately passed from the signed message. + + +## Integration Notes + + +The ERC20PermitMod interacts with the diamond's storage through the `LibERC20Permit.PermitStorage` struct. Facets integrating this module must retrieve this storage using `LibERC20Permit.getPermitStorage()`. The `permit` function within the library validates signatures and updates the allowance, but the responsibility of emitting the `Approval` event lies with the calling facet, ensuring adherence to the ERC-20 standard's event emission requirements. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx new file mode 100644 index 00000000..cf8a7151 --- /dev/null +++ b/website/docs/contracts/modules/ERC6909Mod.mdx @@ -0,0 +1,535 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "LibERC6909 — ERC-6909 Library - Thrown when the sender has insufficient balance." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC6909 — ERC-6909 Library - Thrown when the sender has insufficient balance. + + + +- Implements the core ERC-6909 token logic including minting, burning, transfers, and approvals. +- Supports both fungible and non-fungible token types via token IDs. +- Includes operator functionality, allowing accounts to manage tokens on behalf of others. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC6909Mod provides a standardized implementation for the ERC-6909 token standard, enabling fungible and non-fungible tokens within a Compose diamond. It manages token balances, approvals, and operator relationships, crucial for composable asset management in decentralized applications. + +--- + +## Storage + +### ERC6909Storage + +**Note:** storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. **Parameters** + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. **Parameters** + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. **Returns** + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. **Parameters** + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. **Parameters** + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. **Parameters** + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. **Parameters** +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. **Parameters** +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. **Parameters** +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. **Parameters** +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. **Parameters** +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909} from "@compose/contracts/src/interfaces/IERC6909.sol"; +import {ERC6909Mod} from "@compose/contracts/src/modules/ERC6909/ERC6909Mod.sol"; + +contract MyTokenFacet { + using ERC6909Mod for address; + + address immutable DIAMOND_ADDRESS; + + constructor(address _diamondAddress) { + DIAMOND_ADDRESS = _diamondAddress; + } + + /** + * @notice Mints tokens to a recipient. + * @param _to The address to mint tokens to. + * @param _id The ID of the token to mint. + * @param _amount The amount of tokens to mint. + */ + function mintTokens(address _to, uint256 _id, uint256 _amount) external { + DIAMOND_ADDRESS.mint(_id, _to, _amount); + } + + /** + * @notice Transfers tokens from one address to another. + * @param _from The address to transfer tokens from. + * @param _to The address to transfer tokens to. + * @param _id The ID of the token to transfer. + * @param _amount The amount of tokens to transfer. + */ + function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + DIAMOND_ADDRESS.transfer(_id, _from, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure access control is correctly implemented in facets calling ERC6909Mod functions, especially for sensitive operations like minting and burning. +- Handle potential `ERC6909Mod.InsufficientBalance` errors gracefully in your facets. +- Understand that ERC6909Mod functions operate on the diamond's storage; changes are persistent and upgrade-safe as long as the storage layout is maintained. + + +## Integration Notes + + +The ERC6909Mod interacts directly with the diamond's storage. It defines its own storage slot via `STORAGE_POSITION` to hold the `ERC6909Storage` struct. Facets that use ERC6909Mod must be aware of this storage layout and ensure no conflicts arise with other modules or facets. The `getStorage` function provides a pointer to this struct for direct inspection if needed, but direct manipulation is discouraged in favor of calling the provided functions. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..11ae3b5e --- /dev/null +++ b/website/docs/contracts/modules/ERC721EnumerableMod.mdx @@ -0,0 +1,353 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "ERC-721 Enumerable Library for Compose - Thrown when attempting to interact with a non-existent token." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Enumerable Library for Compose - Thrown when attempting to interact with a non-existent token. + + + +- Automatically updates token enumeration lists during mint, burn, and transfer operations. +- Provides an explicit `getStorage` function to retrieve the ERC-721 enumerable storage struct, useful for debugging or advanced querying. +- Enforces basic validation for token existence, receiver address, and ownership during operations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721EnumerableMod provides essential functionality for managing ERC-721 tokens within a Compose diamond, specifically addressing the enumeration of tokens. It ensures that tokens are correctly added and removed from internal tracking lists during minting, burning, and transfers, maintaining a consistent and auditable token supply. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. **Parameters** + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. **Returns** + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. **Parameters** + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. **Parameters** + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. **Parameters** +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. **Parameters** +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. **Parameters** +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. **Parameters** +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. **Parameters** +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "@compose/modules/ERC721/ERC721EnumerableMod.sol"; +import {IERC721Storage} from "@compose/storage/ERC721Storage.sol"; + +contract MyERC721Facet { + // Assume IERC721EnumerableMod is registered at a specific selector in the diamond. + // Assume IERC721Storage is accessible via diamond storage. + + function mintToken(address _to, uint256 _tokenId) external { + // Access the ERC721EnumerableMod interface + IERC721EnumerableMod enumerableMod = IERC721EnumerableMod(address(this)); + + // Mint the token, which will also update enumeration lists + enumerableMod.mint(_to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + // Access the ERC721EnumerableMod interface + IERC721EnumerableMod enumerableMod = IERC721EnumerableMod(address(this)); + + // Burn the token, which will also remove it from enumeration lists + enumerableMod.burn(_tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + // Access the ERC721EnumerableMod interface + IERC721EnumerableMod enumerableMod = IERC721EnumerableMod(address(this)); + + // Transfer the token, which updates enumeration data + enumerableMod.transferFrom(_from, _to, _tokenId); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented in calling facets for `mint`, `burn`, and `transferFrom` operations, as the module itself does not enforce caller authorization beyond basic checks. +- Always verify that the `_tokenId` exists before attempting to burn it, and that the receiver address is not zero when minting, to prevent unexpected reverts. +- Be mindful of storage layout changes. This module relies on a specific structure for ERC721 enumerable data; any modifications to the underlying storage slot could break its functionality. + + +## Integration Notes + + +The ERC721EnumerableMod interacts with the ERC-721 storage pattern, specifically managing data within a predefined storage slot dedicated to enumerable ERC-721 tokens. The `getStorage` function uses inline assembly to directly access this slot. Any facet that utilizes this module must ensure that the diamond's storage layout includes the correct storage slot for ERC721 enumerable data and that this module is correctly registered within the diamond proxy. The module's operations are designed to be atomic with respect to the token's lifecycle, ensuring that enumeration data is always consistent with the token's existence and ownership. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx new file mode 100644 index 00000000..4d241df7 --- /dev/null +++ b/website/docs/contracts/modules/ERC721Mod.mdx @@ -0,0 +1,365 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "ERC-721 Library for Compose - Thrown when attempting to interact with a non-existent token." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Library for Compose - Thrown when attempting to interact with a non-existent token. + + + +- Supports core ERC-721 operations: minting, burning, and transferring tokens. +- Enforces token existence checks for burn and transfer operations. +- Manages token ownership and approvals as per ERC-721 standard. +- Provides `getStorage` to inspect the module's internal ERC-721 state. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod provides essential ERC-721 token functionalities for Compose diamonds. It enables minting, burning, and transferring of NFTs, ensuring proper ownership and approval management. This module is crucial for implementing non-fungible token standards within a composable diamond architecture, allowing for safe and upgradeable NFT logic. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. **Parameters** + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. **Returns** + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. **Parameters** + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. **Parameters** + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. **Parameters** +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. **Parameters** +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. **Parameters** +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). **Parameters** +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. **Parameters** +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; + +contract MyNFTPartialFacet { + // Assume IERC721Mod is initialized and accessible via the diamond proxy + IERC721Mod public immutable erc721Mod; + + constructor(address _diamondProxy) { + erc721Mod = IERC721Mod(_diamondProxy); + } + + function mintNewToken(address _to, uint256 _tokenId) external { + // Example of calling mint from the module + erc721Mod.mint(_to, _tokenId); + } + + function transferExistingToken(address _from, address _to, uint256 _tokenId) external { + // Example of calling transferFrom from the module + erc721Mod.transferFrom(_from, _to, _tokenId); + } + + function destroyToken(uint256 _tokenId) external { + // Example of calling burn from the module + erc721Mod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Mod` contract is correctly initialized and accessible via the diamond proxy before calling its functions. +- Handle potential reverts from functions like `mint` (token exists, zero address) and `burn`/`transferFrom` (token does not exist) using `try/catch` or by ensuring preconditions are met. +- Be mindful of storage slot collisions if implementing custom ERC-721 logic; refer to `getStorage` for the module's storage layout. + + +## Integration Notes + + +The ERC721Mod utilizes a predefined storage slot for its ERC-721 data structure. Facets interacting with this module should be aware of this storage layout, especially when using the `getStorage` function. Modifications made through `mint`, `burn`, or `transferFrom` directly update this shared storage, making them immediately visible to all facets that access the same storage slot. No specific invariants or ordering requirements beyond standard ERC-721 logic are imposed by this module itself. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx new file mode 100644 index 00000000..01ffde9d --- /dev/null +++ b/website/docs/contracts/modules/NonReentrancyMod.mdx @@ -0,0 +1,141 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. + + + +- Prevents reentrancy by maintaining an internal non-reentrancy lock state. +- Provides explicit `enter` and `exit` functions for clear control flow. +- Designed for integration within Compose diamond facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod module provides essential utilities for preventing reentrancy attacks within your diamond. By utilizing its `enter` and `exit` functions, facets can enforce strict execution order, ensuring that critical operations are not interrupted by recursive calls, thereby enhancing the security and integrity of your diamond's state. + +--- + +## Storage + +--- +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/modules/NonReentrancyMod/LibNonReentrancy.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 public _nonReentrancyState; // Example state protected by non-reentrancy + + /** + * @notice Performs a protected operation. + */ + function protectedOperation() external { + // Enter the non-reentrancy guard + _nonReentrancyState = _nonReentrancyState.enter(); + + // Perform critical state changes here... + // For example, emit an event, update storage, etc. + + // Exit the non-reentrancy guard + _nonReentrancyState = _nonReentrancyState.exit(); + } +}`} + + +## Best Practices + + +- Always pair `enter()` with a corresponding `exit()` call to ensure the guard is properly released, even in error scenarios (e.g., using `try/catch` or `require` statements that might revert before `exit()`). +- Use `LibNonReentrancy.enter()` at the very beginning of a function and `LibNonReentrancy.exit()` at the very end to provide the broadest protection. +- Ensure the variable used with `LibNonReentrancy` is appropriately scoped and managed within the facet's storage. + + +## Integration Notes + + +The NonReentrancyMod is intended to be used as a library within facets. Facets will typically declare a storage variable (e.g., `uint256 _nonReentrancyState`) and use the `LibNonReentrancy` functions with this variable to manage the reentrancy lock. The state managed by this library is local to the facet's execution context and does not directly interact with or modify the diamond's shared storage slots. Each facet using this module is responsible for its own reentrancy protection. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx new file mode 100644 index 00000000..92ad053b --- /dev/null +++ b/website/docs/contracts/modules/OwnerMod.mdx @@ -0,0 +1,251 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. + + + +- Manages contract ownership according to ERC-173 standards. +- Provides a `requireOwner` modifier for access control. +- Supports ownership renouncement by transferring to `address(0)`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerMod provides essential ERC-173 contract ownership functionality. It manages the contract owner's address, allowing for secure ownership transfers and owner-based access control, crucial for administrative operations within a diamond. + +--- + +## Storage + +### OwnerStorage + +**Note:** storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. **Returns** + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner **Returns** + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. **Parameters** + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; +import {OwnerStorage} from "@compose/modules/owner/OwnerStorage.sol"; + +contract MyOwnerFacet { + address constant STORAGE_POSITION = 0x0; // Example storage slot + + function getOwner() external view returns (address) { + // Access the OwnerMod storage layout + OwnerStorage storage ownerStorage = OwnerStorage(STORAGE_POSITION); + // Call the owner function from the module interface + return IOwnerMod(address(ownerStorage)).owner(); + } + + function transferContractOwnership(address _newOwner) external { + OwnerStorage storage ownerStorage = OwnerStorage(STORAGE_POSITION); + // Call the transfer ownership function from the module interface + IOwnerMod(address(ownerStorage)).transferOwnership(_newOwner); + } +}`} + + +## Best Practices + + +- Use `requireOwner()` to protect sensitive administrative functions within your facet. +- Carefully manage ownership transfers, ensuring the new owner is a trusted address. +- Implement checks for `address(0)` when transferring ownership to allow for renouncement. + + +## Integration Notes + + +The OwnerMod utilizes a specific storage slot for its `OwnerStorage` struct. Facets interacting with ownership functions must access this storage slot directly or via the `IOwnerMod` interface. The `getStorage` function can be used to retrieve a pointer to this storage if the `STORAGE_POSITION` is known. Changes to the owner address are immediately reflected across all facets interacting with the `OwnerStorage`. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..5f1ec77a --- /dev/null +++ b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx @@ -0,0 +1,322 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. + + + +- Two-step ownership transfer process for enhanced security. +- `requireOwner` modifier for access control to critical functions. +- Provides standard `owner()` and `pendingOwner()` view functions. +- Supports `renounceOwnership()` to set owner to `address(0)`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerTwoStepsMod provides secure, two-step ownership management for Compose diamonds. This pattern prevents accidental ownership loss by requiring explicit acceptance of a new owner, enhancing contract safety and upgradeability. + +--- + +## Storage + +### OwnerStorage + +**Note:** storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +**Note:** storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +Storage position: `OWNER_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. **Returns** + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. **Returns** + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. **Parameters** + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {OwnerTwoStepsMod} from "@compose/modules/owner/OwnerTwoStepsMod.sol"; +import {IOwnerTwoStepsMod} from "@compose/modules/owner/IOwnerTwoStepsMod.sol"; + +contract MyFacet { + OwnerTwoStepsMod private ownerMod; + + // Assume OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are defined and initialized + uint256 constant OWNER_STORAGE_POSITION = 1; // Example slot + uint256 constant PENDING_OWNER_STORAGE_POSITION = 2; // Example slot + + constructor(address diamondProxy) { + ownerMod = new OwnerTwoStepsMod(diamondProxy, OWNER_STORAGE_POSITION, PENDING_OWNER_STORAGE_POSITION); + } + + /** + * @notice Transfers ownership to a new address. + * @param _newOwner The address to transfer ownership to. + */ + function initiateOwnershipTransfer(address _newOwner) external { + ownerMod.transferOwnership(_newOwner); + } + + /** + * @notice Accepts pending ownership transfer. + */ + function acceptNewOwnership() external { + ownerMod.acceptOwnership(); + } + + /** + * @notice Gets the current owner. + * @return The current owner address. + */ + function getCurrentOwner() external view returns (address) { + return ownerMod.owner(); + } + + /** + * @notice Renounces ownership of the contract. + */ + function removeOwnership() external { + ownerMod.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Always use the `transferOwnership` function to initiate a transfer, followed by the new owner calling `acceptOwnership`. +- Protect sensitive functions by calling `requireOwner()` to ensure only the current owner can execute them. +- Ensure `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are unique and correctly set during diamond initialization to prevent storage collisions. + + +## Integration Notes + + +This module utilizes inline assembly to read and write to specific storage slots defined by `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION`. These slots must be unique and managed by the diamond proxy's storage layout. Facets integrating this module should ensure these positions do not conflict with other facets' storage. The module directly manipulates the owner and pending owner state, making these accessible to any facet that queries the module or the diamond's storage directly. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx new file mode 100644 index 00000000..fc7268c6 --- /dev/null +++ b/website/docs/contracts/modules/RoyaltyMod.mdx @@ -0,0 +1,367 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "LibRoyalty - ERC-2981 Royalty Standard Library - Thrown when default royalty fee exceeds 100% (10000 basis points)." +gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibRoyalty - ERC-2981 Royalty Standard Library - Thrown when default royalty fee exceeds 100% (10000 basis points). + + + +- Implements the ERC-2981 `royaltyInfo` standard, providing a standardized way to query royalty details. +- Supports both a global default royalty setting and token-specific overrides, offering flexible royalty management. +- Includes functions to explicitly delete or reset royalty information, allowing for dynamic adjustments to royalty configurations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The RoyaltyMod provides an implementation of the ERC-2981 royalty standard for Compose diamonds. It allows setting default royalties and token-specific overrides, ensuring that creators are compensated on secondary sales. This module is crucial for marketplaces and NFT platforms built on Compose, enabling composable royalty logic. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +**Note:** storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. **Returns** + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. **Parameters** + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. **Parameters** **Returns** + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. **Parameters** + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. **Parameters** + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). **Parameters** +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. **Parameters** +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). **Parameters** +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. **Parameters** +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "@compose/contracts/modules/royalty/IRoyaltyMod.sol"; + +contract MyRoyaltyFacet { + // Assume IRoyaltyMod is correctly implemented and accessible via the diamond proxy + IRoyaltyMod internal royaltyMod; + + // Initialize with the diamond proxy address + function initialize(address _diamondProxy) public { + royaltyMod = IRoyaltyMod(_diamondProxy); + } + + /** + * @notice Sets a default royalty for all tokens. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). + */ + function grantDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setDefaultRoyalty(_receiver, _feeBasisPoints); + } + + /** + * @notice Sets a specific royalty for a token ID. + * @param _tokenId The ID of the token to set royalty for. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). + */ + function grantTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Retrieves royalty information for a token and sale price. + * @param _tokenId The ID of the token. + * @param _salePrice The sale price of the token. + * @return receiver The address entitled to the royalty. + * @return royaltyAmount The calculated royalty amount. + */ + function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { + (receiver, royaltyAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); + return (receiver, royaltyAmount); + } +} +`} + + +## Best Practices + + +- Ensure the `_feeBasisPoints` parameter in `setDefaultRoyalty` and `setTokenRoyalty` does not exceed 10000 (100%) to prevent excessive fees. The library enforces this, but explicit checks in calling facets can add an extra layer of safety. +- When resetting token-specific royalties using `resetTokenRoyalty`, be aware that it reverts to the default royalty settings. Ensure the default royalty is configured appropriately if this action is intended. +- Use custom errors or specific revert messages when calling functions to clearly indicate the reason for failure, especially during royalty setting operations. + + +## Integration Notes + + +The RoyaltyMod interacts with diamond storage to manage default royalty information and token-specific royalty configurations. Facets can access this module via its interface, and calls to functions like `royaltyInfo` will transparently query the appropriate storage slots within the diamond's storage layout. The `getStorage` function provides direct access to the internal storage struct, which is useful for advanced inspection or complex logic but should be used with caution to maintain storage hygiene. Changes to default royalties are immediately reflected in subsequent `royaltyInfo` calls for tokens without specific overrides. Token-specific royalties directly override the default for the specified token ID. + + +
+ +
+ + From 5477ea0dd8cd71255ea3c4192313bd98bee6d747 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 16:50:39 -0500 Subject: [PATCH 28/68] improve PR content --- .github/scripts/generate-docs-utils/pr-body-generator.js | 9 ++++++--- .github/workflows/docs-generate.yml | 7 +++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/scripts/generate-docs-utils/pr-body-generator.js b/.github/scripts/generate-docs-utils/pr-body-generator.js index 2a52d66b..fcba50e5 100644 --- a/.github/scripts/generate-docs-utils/pr-body-generator.js +++ b/.github/scripts/generate-docs-utils/pr-body-generator.js @@ -22,8 +22,11 @@ function generatePRBody(summary) { const modules = summary.modules || []; const total = summary.totalGenerated || 0; - let body = '## Auto-Generated Contract Documentation\n\n'; - body += 'This PR contains auto-generated documentation from contract comments using `forge doc`.\n\n'; + let body = '## Auto-Generated Docs Pages\n\n'; + body += 'This PR contains auto-generated documentation from contract comments using `forge doc`.'; + body += 'The output is passed through AI to enhance the documentation content and add additional information.\n'; + body += 'Please ALWAYS review the generated content and ensure it is accurate and complete to Compose Standards.\n'; + body += '### Summary\n'; body += `- **Total generated:** ${total} files\n\n`; @@ -57,7 +60,7 @@ function generatePRBody(summary) { body += '- [ ] Ensure consistency with existing docs\n\n'; body += '---\n'; - body += ' Date: Fri, 19 Dec 2025 17:22:27 -0500 Subject: [PATCH 29/68] improve forge doc extraction --- .../doc-generation-utils.js | 180 +++++++++++++++--- .../generate-docs-utils/forge-doc-parser.js | 49 ++++- .../templates/pages/contract.mdx.template | 20 +- .../templates/templates.js | 65 ++++++- .github/scripts/generate-docs.js | 22 ++- .../theme/EditThisPage/DocsEditThisPage.js | 21 +- .../theme/EditThisPage/SimpleEditThisPage.js | 1 - 7 files changed, 296 insertions(+), 62 deletions(-) diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js index d2f873e0..75f08453 100644 --- a/.github/scripts/generate-docs-utils/doc-generation-utils.js +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -122,10 +122,34 @@ function extractModuleNameFromPath(filePath) { return path.basename(filePath, path.extname(filePath)); } +/** + * Check if a line is a code element declaration (event, error, function, struct, etc.) + * @param {string} line - Trimmed line to check + * @returns {boolean} True if line is a code element declaration + */ +function isCodeElementDeclaration(line) { + if (!line) return false; + return ( + line.startsWith('function ') || + line.startsWith('error ') || + line.startsWith('event ') || + line.startsWith('struct ') || + line.startsWith('enum ') || + line.startsWith('contract ') || + line.startsWith('library ') || + line.startsWith('interface ') || + line.startsWith('modifier ') || + /^\w+\s+(constant|immutable)\s/.test(line) || + /^(bytes32|uint\d*|int\d*|address|bool|string)\s+constant\s/.test(line) + ); +} + /** * Extract module description from source file NatSpec comments + * Only extracts TRUE file-level comments (those with @title, or comments not immediately followed by code elements) + * Skips comments that belong to events, errors, functions, etc. * @param {string} solFilePath - Path to the Solidity source file - * @returns {string} Description extracted from @title and @notice tags + * @returns {string} Description extracted from @title and @notice tags, or empty string */ function extractModuleDescriptionFromSource(solFilePath) { const content = readFileSafe(solFilePath); @@ -135,10 +159,10 @@ function extractModuleDescriptionFromSource(solFilePath) { const lines = content.split('\n'); let inComment = false; + let commentStartLine = -1; let commentBuffer = []; let title = ''; let notice = ''; - let foundFirstCodeElement = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; @@ -149,30 +173,16 @@ function extractModuleDescriptionFromSource(solFilePath) { continue; } - // Check if we've reached the first function/constant/error (end of file-level comments) - if (trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && !trimmed.startsWith('*') && - (trimmed.startsWith('function ') || trimmed.startsWith('error ') || trimmed.startsWith('event ') || - trimmed.startsWith('struct ') || trimmed.startsWith('enum ') || trimmed.match(/^\w+\s+constant/))) { - foundFirstCodeElement = true; - // If we're in a comment, finish processing it first - if (inComment) { - // Process the comment we were collecting - const commentText = commentBuffer.join(' '); - const titleMatch = commentText.match(/@title\s+(.+?)(?:\s+@|\s*\*\/|$)/); - if (titleMatch) { - title = titleMatch[1].trim(); - } - const noticeMatch = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*\*\/|$)/); - if (noticeMatch) { - notice = noticeMatch[1].trim(); - } - } + // Check if we've reached a code element without finding a file-level comment + if (!inComment && isCodeElementDeclaration(trimmed)) { + // We hit code without finding a file-level comment break; } // Start of block comment - if (trimmed.startsWith('/*')) { + if (trimmed.startsWith('/**') || trimmed.startsWith('/*')) { inComment = true; + commentStartLine = i; commentBuffer = []; continue; } @@ -182,18 +192,42 @@ function extractModuleDescriptionFromSource(solFilePath) { inComment = false; const commentText = commentBuffer.join(' '); - // Extract @title - const titleMatch = commentText.match(/@title\s+(.+?)(?:\s+@|\s*\*\/|$)/); + // Look ahead to see if next non-empty line is a code element + let nextCodeLine = ''; + for (let j = i + 1; j < lines.length && j < i + 5; j++) { + const nextTrimmed = lines[j].trim(); + if (nextTrimmed && !nextTrimmed.startsWith('//') && !nextTrimmed.startsWith('/*')) { + nextCodeLine = nextTrimmed; + break; + } + } + + // If the comment has @title, it's a file-level comment + const titleMatch = commentText.match(/@title\s+(.+?)(?:\s+@|\s*$)/); if (titleMatch) { title = titleMatch[1].trim(); + const noticeMatch = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*$)/); + if (noticeMatch) { + notice = noticeMatch[1].trim(); + } + break; // Found file-level comment, stop searching } - - // Extract @notice - const noticeMatch = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*\*\/|$)/); - if (noticeMatch) { - notice = noticeMatch[1].trim(); + + // If next line is a code element (event, error, function, etc.), + // this comment belongs to that element, not the file + if (isCodeElementDeclaration(nextCodeLine)) { + // This is an item-level comment, skip it and continue looking + commentBuffer = []; + continue; } - + + // If it's a standalone comment with @notice (no code element following), use it + const standaloneNotice = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*$)/); + if (standaloneNotice && !isCodeElementDeclaration(nextCodeLine)) { + notice = standaloneNotice[1].trim(); + break; + } + commentBuffer = []; continue; } @@ -220,6 +254,93 @@ function extractModuleDescriptionFromSource(solFilePath) { return ''; } +/** + * Generate a meaningful description from module/facet name when no source description exists + * @param {string} contractName - Name of the contract (e.g., "AccessControlMod", "ERC20Facet") + * @returns {string} Generated description + */ +function generateDescriptionFromName(contractName) { + if (!contractName) return ''; + + // Remove common suffixes + let baseName = contractName + .replace(/Mod$/, '') + .replace(/Module$/, '') + .replace(/Facet$/, ''); + + // Add spaces before capitals (CamelCase to spaces) + const readable = baseName + .replace(/([A-Z])/g, ' $1') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // Handle acronyms like ERC20 + .trim(); + + // Detect contract type + const isModule = contractName.endsWith('Mod') || contractName.endsWith('Module'); + const isFacet = contractName.endsWith('Facet'); + + // Generate description based on known patterns + const lowerName = baseName.toLowerCase(); + + // Common patterns + if (lowerName.includes('accesscontrol')) { + if (lowerName.includes('pausable')) { + return `Role-based access control with pause functionality for Compose diamonds`; + } + if (lowerName.includes('temporal')) { + return `Time-limited role-based access control for Compose diamonds`; + } + return `Role-based access control (RBAC) ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.startsWith('erc20')) { + const variant = baseName.replace(/^ERC20/, '').trim(); + if (variant) { + return `ERC-20 token ${variant.toLowerCase()} ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + return `ERC-20 fungible token ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.startsWith('erc721')) { + const variant = baseName.replace(/^ERC721/, '').trim(); + if (variant) { + return `ERC-721 NFT ${variant.toLowerCase()} ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + return `ERC-721 non-fungible token ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.startsWith('erc1155')) { + return `ERC-1155 multi-token ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.startsWith('erc6909')) { + return `ERC-6909 minimal multi-token ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.includes('owner')) { + if (lowerName.includes('twostep')) { + return `Two-step ownership transfer ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + return `Ownership management ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.includes('diamond')) { + if (lowerName.includes('cut')) { + return `Diamond upgrade (cut) ${isModule ? 'module' : 'facet'} for ERC-2535 diamonds`; + } + if (lowerName.includes('loupe')) { + return `Diamond introspection (loupe) ${isModule ? 'module' : 'facet'} for ERC-2535 diamonds`; + } + return `Diamond core ${isModule ? 'module' : 'facet'} for ERC-2535 implementation`; + } + if (lowerName.includes('royalty')) { + return `ERC-2981 royalty ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.includes('nonreentran') || lowerName.includes('reentrancy')) { + return `Reentrancy guard ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + if (lowerName.includes('erc165')) { + return `ERC-165 interface detection ${isModule ? 'module' : 'facet'} for Compose diamonds`; + } + + // Generic fallback + const typeLabel = isModule ? 'module' : isFacet ? 'facet' : 'contract'; + return `${readable} ${typeLabel} for Compose diamonds`; +} + /** * Determine if a contract is a module or facet * Modules are Solidity files whose top-level code lives outside of contracts and Solidity libraries. @@ -285,6 +406,7 @@ module.exports = { readChangedFilesFromFile, extractModuleNameFromPath, extractModuleDescriptionFromSource, + generateDescriptionFromName, }; diff --git a/.github/scripts/generate-docs-utils/forge-doc-parser.js b/.github/scripts/generate-docs-utils/forge-doc-parser.js index 4a79978a..3d1cc043 100644 --- a/.github/scripts/generate-docs-utils/forge-doc-parser.js +++ b/.github/scripts/generate-docs-utils/forge-doc-parser.js @@ -51,7 +51,7 @@ function parseForgeDocMarkdown(content, filePath) { // Parse description (first non-empty lines after title, before sections) if (data.title && !currentSection && trimmedLine && !line.startsWith('#') && !line.startsWith('[')) { - const sanitizedLine = sanitizeBrokenLinks(trimmedLine); + const sanitizedLine = cleanDescription(sanitizeBrokenLinks(trimmedLine)); if (!data.description) { data.description = sanitizedLine; data.subtitle = sanitizedLine; @@ -142,7 +142,7 @@ function parseForgeDocMarkdown(content, filePath) { // End description collection on special markers if (trimmedLine.startsWith('**Parameters**') || trimmedLine.startsWith('**Returns**')) { if (descriptionBuffer.length > 0) { - const description = sanitizeBrokenLinks(descriptionBuffer.join(' ').trim()); + const description = cleanDescription(sanitizeBrokenLinks(descriptionBuffer.join(' ').trim())); currentItem.description = description; currentItem.notice = description; descriptionBuffer = []; @@ -297,6 +297,47 @@ function sanitizeBrokenLinks(text) { return text.replace(/\[([^\]]+)\]\(\/src\/[^\)]+\)/g, '$1'); } +/** + * Clean description text by removing markdown artifacts + * Strips **Parameters**, **Returns**, **Note:** and other section markers + * that get incorrectly included in descriptions from forge doc output + * @param {string} text - Description text that may contain markdown artifacts + * @returns {string} Cleaned description text + */ +function cleanDescription(text) { + if (!text) return text; + + let cleaned = text; + + // Remove markdown section headers that shouldn't be in descriptions + // These patterns appear when forge doc parsing doesn't stop at section boundaries + const artifactPatterns = [ + /\s*\*\*Parameters\*\*\s*/g, + /\s*\*\*Returns\*\*\s*/g, + /\s*\*\*Note:\*\*\s*/g, + /\s*\*\*Events\*\*\s*/g, + /\s*\*\*Errors\*\*\s*/g, + /\s*\*\*See Also\*\*\s*/g, + /\s*\*\*Example\*\*\s*/g, + ]; + + for (const pattern of artifactPatterns) { + cleaned = cleaned.replace(pattern, ' '); + } + + // Remove @custom: tags that may leak through (e.g., "@custom:error AccessControlUnauthorizedAccount") + cleaned = cleaned.replace(/@custom:\w+\s+/g, ''); + + // Clean up "error: ErrorName" patterns that appear inline + // Keep the error name but format it better: "error: ErrorName If..." -> "Reverts with ErrorName if..." + cleaned = cleaned.replace(/\berror:\s+(\w+)\s+/gi, 'Reverts with $1 '); + + // Normalize whitespace: collapse multiple spaces, trim + cleaned = cleaned.replace(/\s+/g, ' ').trim(); + + return cleaned; +} + /** * Extract storage information from parsed data * @param {object} data - Parsed documentation data @@ -543,9 +584,9 @@ function parseIndividualItemFile(content, filePath) { } } - // Combine description buffer + // Combine description buffer and clean it if (descriptionBuffer.length > 0) { - description = sanitizeBrokenLinks(descriptionBuffer.join(' ').trim()); + description = cleanDescription(sanitizeBrokenLinks(descriptionBuffer.join(' ').trim())); } // For constants, return array of constant objects diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index de4ac1c7..b5ba3e84 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -29,7 +29,7 @@ import GradientButton from '@site/src/components/ui/GradientButton'; {{#if keyFeatures}} -{{sanitizeMdx keyFeatures}} +{{{sanitizeMdx keyFeatures}}} {{/if}} @@ -41,7 +41,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -{{sanitizeMdx overview}} +{{{sanitizeMdx overview}}} --- @@ -52,7 +52,7 @@ This module provides internal functions for use in your custom facets. Import it ### {{name}} {{#if description}} -{{sanitizeMdx description}} +{{{sanitizeMdx description}}} {{/if}} {{#if definition}} @@ -70,7 +70,7 @@ This module provides internal functions for use in your custom facets. Import it {{#if hasStorage}} {{#if storageInfo}} -{{sanitizeMdx storageInfo}} +{{{sanitizeMdx storageInfo}}} {{/if}} --- {{#if hasStateVariables}} @@ -98,7 +98,7 @@ This module provides internal functions for use in your custom facets. Import it ### {{name}} {{#if description}} -{{sanitizeMdx description}} +{{{sanitizeMdx description}}} {{/if}} {{#if signature}} @@ -155,7 +155,7 @@ This module provides internal functions for use in your custom facets. Import it {{#if description}}
- {{sanitizeMdx description}} + {{{sanitizeMdx description}}}
{{/if}} @@ -198,7 +198,7 @@ This module provides internal functions for use in your custom facets. Import it {{#if description}}
- {{sanitizeMdx description}} + {{{sanitizeMdx description}}}
{{/if}} @@ -227,7 +227,7 @@ This module provides internal functions for use in your custom facets. Import it ## Best Practices -{{sanitizeMdx bestPractices}} +{{{sanitizeMdx bestPractices}}} {{/if}} @@ -236,7 +236,7 @@ This module provides internal functions for use in your custom facets. Import it ## Security Considerations -{{sanitizeMdx securityConsiderations}} +{{{sanitizeMdx securityConsiderations}}} {{/if}} {{/if}} @@ -246,7 +246,7 @@ This module provides internal functions for use in your custom facets. Import it ## Integration Notes -{{sanitizeMdx integrationNotes}} +{{{sanitizeMdx integrationNotes}}} {{/if}} {{/if}} diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index fc45172f..e1cd3639 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -376,6 +376,67 @@ function validateData(data) { } } +/** + * Generate fallback description for state variables/constants based on naming patterns + * @param {string} name - Variable name (e.g., "STORAGE_POSITION", "DEFAULT_ADMIN_ROLE") + * @param {string} moduleName - Name of the module/contract for context + * @returns {string} Generated description or empty string + */ +function generateStateVariableDescription(name, moduleName) { + if (!name) return ''; + + const upperName = name.toUpperCase(); + + // Common patterns for diamond/ERC contracts + const patterns = { + // Storage position patterns + 'STORAGE_POSITION': 'Diamond storage slot position for this module', + 'STORAGE_SLOT': 'Diamond storage slot identifier', + '_STORAGE_POSITION': 'Diamond storage slot position', + '_STORAGE_SLOT': 'Diamond storage slot identifier', + + // Role patterns + 'DEFAULT_ADMIN_ROLE': 'Default administrative role identifier (bytes32(0))', + 'ADMIN_ROLE': 'Administrative role identifier', + 'MINTER_ROLE': 'Minter role identifier', + 'PAUSER_ROLE': 'Pauser role identifier', + 'BURNER_ROLE': 'Burner role identifier', + + // ERC patterns + 'INTERFACE_ID': 'ERC-165 interface identifier', + 'EIP712_DOMAIN': 'EIP-712 domain separator', + 'PERMIT_TYPEHASH': 'EIP-2612 permit type hash', + + // Reentrancy patterns + 'NON_REENTRANT_SLOT': 'Reentrancy guard storage slot', + '_NOT_ENTERED': 'Reentrancy status: not entered', + '_ENTERED': 'Reentrancy status: entered', + }; + + // Check exact matches first + if (patterns[upperName]) { + return patterns[upperName]; + } + + // Check partial matches + if (upperName.includes('STORAGE') && (upperName.includes('POSITION') || upperName.includes('SLOT'))) { + return 'Diamond storage slot position for this module'; + } + if (upperName.includes('_ROLE')) { + const roleName = name.replace(/_ROLE$/i, '').replace(/_/g, ' ').toLowerCase(); + return `${roleName.charAt(0).toUpperCase() + roleName.slice(1)} role identifier`; + } + if (upperName.includes('TYPEHASH')) { + return 'Type hash for EIP-712 structured data'; + } + if (upperName.includes('INTERFACE')) { + return 'ERC-165 interface identifier'; + } + + // Generic fallback + return ''; +} + /** * Prepare base data common to both facet and module templates * @param {object} data - Documentation data @@ -416,12 +477,12 @@ function prepareBaseData(data, position = 99) { structs: (data.structs || []).map(prepareStructData), hasStructs: (data.structs || []).length > 0, - // State variables (for modules) + // State variables (for modules) - with fallback description generation stateVariables: (data.stateVariables || []).map(v => ({ name: v.name, type: v.type || '', value: v.value || '', - description: v.description || '', + description: v.description || generateStateVariableDescription(v.name, data.title), })), hasStateVariables: (data.stateVariables || []).length > 0, hasStorage: Boolean(data.storageInfo || (data.stateVariables && data.stateVariables.length > 0)), diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index 8ce02168..9afe2dda 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -19,6 +19,7 @@ const { readChangedFilesFromFile, extractModuleNameFromPath, extractModuleDescriptionFromSource, + generateDescriptionFromName, } = require('./generate-docs-utils/doc-generation-utils'); const { readFileSafe, writeFileSafe } = require('./workflow-utils'); const { @@ -183,12 +184,21 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { data.subtitle = sourceDescription; data.overview = sourceDescription; } else { - // Use a generic description if no source description found - const genericDescription = `Module providing internal functions for ${data.title}`; - if (!data.description || data.description.includes('Event emitted') || data.description.includes('Thrown when')) { - data.description = genericDescription; - data.subtitle = genericDescription; - data.overview = genericDescription; + // Use smart description generator based on contract name + // This handles cases where source file has no file-level @title/@notice + const generatedDescription = generateDescriptionFromName(data.title); + if (generatedDescription) { + data.description = generatedDescription; + data.subtitle = generatedDescription; + data.overview = generatedDescription; + } else { + // Last resort fallback + const genericDescription = `Module providing internal functions for ${data.title}`; + if (!data.description || data.description.includes('Event emitted') || data.description.includes('Thrown when')) { + data.description = genericDescription; + data.subtitle = genericDescription; + data.overview = genericDescription; + } } } diff --git a/website/src/theme/EditThisPage/DocsEditThisPage.js b/website/src/theme/EditThisPage/DocsEditThisPage.js index e0697a14..3ed1ec80 100644 --- a/website/src/theme/EditThisPage/DocsEditThisPage.js +++ b/website/src/theme/EditThisPage/DocsEditThisPage.js @@ -25,19 +25,20 @@ export default function DocsEditThisPage({editUrl}) { return (
{viewSource && ( - - - View Source - + <> + + View Source + + | + )} {editUrl && ( - Edit this page )} diff --git a/website/src/theme/EditThisPage/SimpleEditThisPage.js b/website/src/theme/EditThisPage/SimpleEditThisPage.js index ad5a8553..eb7d676c 100644 --- a/website/src/theme/EditThisPage/SimpleEditThisPage.js +++ b/website/src/theme/EditThisPage/SimpleEditThisPage.js @@ -16,7 +16,6 @@ export default function SimpleEditThisPage({editUrl}) { return (
- Edit this page
From 8fd3d0c28913baf3b948e02cb6af2111875ad106 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 17:23:34 -0500 Subject: [PATCH 30/68] remove old page for regen --- .../contracts/facets/AccessControlFacet.mdx | 561 ------------- .../facets/AccessControlPausableFacet.mdx | 330 -------- .../facets/AccessControlTemporalFacet.mdx | 461 ----------- .../docs/contracts/facets/DiamondCutFacet.mdx | 425 ---------- .../contracts/facets/DiamondLoupeFacet.mdx | 255 ------ .../docs/contracts/facets/ERC1155Facet.mdx | 671 ---------------- .../contracts/facets/ERC20BridgeableFacet.mdx | 417 ---------- .../docs/contracts/facets/ERC20BurnFacet.mdx | 276 ------- website/docs/contracts/facets/ERC20Facet.mdx | 594 -------------- .../contracts/facets/ERC20PermitFacet.mdx | 337 -------- .../docs/contracts/facets/ERC6909Facet.mdx | 529 ------------- .../docs/contracts/facets/ERC721BurnFacet.mdx | 221 ------ .../facets/ERC721EnumerableBurnFacet.mdx | 224 ------ .../facets/ERC721EnumerableFacet.mdx | 743 ------------------ website/docs/contracts/facets/ERC721Facet.mdx | 669 ---------------- .../docs/contracts/facets/ExampleDiamond.mdx | 146 ---- website/docs/contracts/facets/OwnerFacet.mdx | 212 ----- .../contracts/facets/OwnerTwoStepsFacet.mdx | 287 ------- .../docs/contracts/facets/RoyaltyFacet.mdx | 211 ----- .../contracts/modules/AccessControlMod.mdx | 447 ----------- .../modules/AccessControlPausableMod.mdx | 384 --------- .../modules/AccessControlTemporalMod.mdx | 484 ------------ .../docs/contracts/modules/DiamondCutMod.mdx | 385 --------- website/docs/contracts/modules/DiamondMod.mdx | 236 ------ website/docs/contracts/modules/ERC1155Mod.mdx | 624 --------------- website/docs/contracts/modules/ERC165Mod.mdx | 159 ---- .../contracts/modules/ERC20BridgeableMod.mdx | 439 ----------- website/docs/contracts/modules/ERC20Mod.mdx | 425 ---------- .../docs/contracts/modules/ERC20PermitMod.mdx | 279 ------- website/docs/contracts/modules/ERC6909Mod.mdx | 535 ------------- .../contracts/modules/ERC721EnumerableMod.mdx | 353 --------- website/docs/contracts/modules/ERC721Mod.mdx | 365 --------- .../contracts/modules/NonReentrancyMod.mdx | 141 ---- website/docs/contracts/modules/OwnerMod.mdx | 251 ------ .../contracts/modules/OwnerTwoStepsMod.mdx | 322 -------- website/docs/contracts/modules/RoyaltyMod.mdx | 367 --------- 36 files changed, 13765 deletions(-) delete mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721Facet.mdx delete mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx delete mode 100644 website/docs/contracts/facets/OwnerFacet.mdx delete mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx delete mode 100644 website/docs/contracts/modules/AccessControlMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondMod.mdx delete mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC165Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC20Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx delete mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC721Mod.mdx delete mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx deleted file mode 100644 index bb44400e..00000000 --- a/website/docs/contracts/facets/AccessControlFacet.mdx +++ /dev/null @@ -1,561 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Contract documentation for AccessControlFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for AccessControlFacet - - - -- Role-based access control (RBAC) for granular permission management. -- Support for granting and revoking roles to individual accounts or batches. -- Ability to define and manage role hierarchies through `setRoleAdmin`. -- Built-in checks (`requireRole`) to enforce access control at the function call level. - - -## Overview - -The AccessControlFacet provides a robust, role-based access control (RBAC) system for Compose diamonds. It enables granular permission management, allowing administrators to grant, revoke, and manage roles for accounts, ensuring that sensitive operations can only be performed by authorized entities. This facet is fundamental for securing diamond functionality. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. error: AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. error: AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. error: AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondProxy, DiamondInit} from "@compose/diamond/contracts/Diamond.sol"; -import {AccessControlFacet} from "@compose/access-control/contracts/AccessControlFacet.sol"; - -contract MyDiamondInit is DiamondInit { - function init() public override { - // ... other initializations ... - - // Initialize AccessControlFacet - AccessControlFacet accessControl = AccessControlFacet(address(this)); - bytes32 adminRole = keccak256(abi.encodePacked("ROLE_ADMIN")); - bytes32 defaultAdminRole = accessControl.getRoleAdmin(adminRole); - - // Grant default admin role to the deployer - accessControl.grantRole(defaultAdminRole, msg.sender); - } -} - -contract MyDiamond is DiamondProxy { - // Assuming AccessControlFacet is deployed and added to the diamond - // ... - - function grantAdminRoleToUser(address _user) external { - AccessControlFacet accessControl = AccessControlFacet(address(this)); - bytes32 adminRole = keccak256(abi.encodePacked("ROLE_ADMIN")); - // Caller must have the admin role to grant other roles - accessControl.grantRole(adminRole, _user); - } - - function executeSensitiveOperation() external { - AccessControlFacet accessControl = AccessControlFacet(address(this)); - bytes32 sensitiveOperationRole = keccak256(abi.encodePacked("SENSITIVE_OPERATION_ROLE")); - // Require the caller to have the specific role - accessControl.requireRole(sensitiveOperationRole, msg.sender); - - // ... perform sensitive operation ... - } -}`} - - -## Best Practices - - -- Initialize roles and grant initial administrative privileges during diamond deployment via `DiamondInit`. -- Define distinct roles for different permission levels and grant them judiciously using `grantRole` or `grantRoleBatch`. -- Use `requireRole` extensively within facet functions to enforce access control checks before executing sensitive logic. - - -## Security Considerations - - -Ensure that the initial administrative roles are granted only to trusted addresses during deployment. Be cautious when granting broad roles, and always use `requireRole` at the entry point of functions that require specific permissions. The `renounceRole` function should be used with care, as it permanently removes the caller's access to a role. The caller must be the current admin of the role to set a new admin role via `setRoleAdmin`. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx deleted file mode 100644 index 7568e61e..00000000 --- a/website/docs/contracts/facets/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,330 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Contract documentation for AccessControlPausableFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for AccessControlPausableFacet - - - -- Self-contained facet with no imports or inheritance -- Only `external` and `internal` function visibility -- Follows Compose readability-first conventions -- Ready for diamond integration - - -## Overview - -Documentation for AccessControlPausableFacet. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx deleted file mode 100644 index fbd6669f..00000000 --- a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,461 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Contract documentation for AccessControlTemporalFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for AccessControlTemporalFacet - - - -- Grants roles with specific expiry timestamps, enabling temporary permissions. -- Provides functions to check if a role has expired (`isRoleExpired`). -- Allows for revocation of time-bound roles before their natural expiry (`revokeTemporalRole`). -- Enforces authorization checks, ensuring only role admins can manage temporal role assignments. - - -## Overview - -The AccessControlTemporalFacet extends the diamond's access control capabilities by introducing time-bound role assignments. It allows for granting and revoking roles with specific expiry timestamps, enhancing dynamic permission management within the diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. error: AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; -import {IAccessControlTemporal} from "./interfaces/IAccessControlTemporal.sol"; - -contract Deployer { - address public diamondAddress; - - function deployDiamond() public { - // ... diamond deployment logic ... - diamondAddress = address(0xYourDiamondAddress); // Replace with actual diamond address - } - - function grantTemporaryRole() public { - IAccessControlTemporal(diamondAddress).grantRoleWithExpiry( - IAccessControlTemporal.Role.Admin, - address(0xUserAddress), // Replace with user address - block.timestamp + 3600 // Role expires in 1 hour - ); - } - - function checkRole() public view returns (bool) { - return IAccessControlTemporal(diamondAddress).isRoleExpired( - IAccessControlTemporal.Role.Admin, - address(0xUserAddress) // Replace with user address - ); - } - - function revokeRole() public { - IAccessControlTemporal(diamondAddress).revokeTemporalRole( - IAccessControlTemporal.Role.Admin, - address(0xUserAddress) // Replace with user address - ); - } -}`} - - -## Best Practices - - -- Initialize the `AccessControlTemporalFacet` with appropriate admin roles during diamond deployment. -- Ensure the `AccessControlFacet` is also deployed and configured for fundamental role management. -- Utilize `grantRoleWithExpiry` for temporary administrative access and `revokeTemporalRole` for immediate removal. - - -## Security Considerations - - -Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role manipulation. The `requireValidRole` function checks for both existence and expiry, mitigating risks associated with stale or expired permissions. Reentrancy is not a concern as these functions do not make external calls. Input validation is handled by the underlying access control mechanisms and explicit checks within the functions. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx deleted file mode 100644 index 2d794540..00000000 --- a/website/docs/contracts/facets/DiamondCutFacet.mdx +++ /dev/null @@ -1,425 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Add=0, Replace=1, Remove=2" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Add=0, Replace=1, Remove=2 - - - -- Supports adding new functions by linking selectors to new facet addresses. -- Enables updating existing functionality by replacing old facet addresses with new ones for specific selectors. -- Allows removal of functions by unlinking selectors, effectively disabling them. -- Facilitates atomic upgrades by allowing a function to be executed immediately after the cut. - - -## Overview - -The DiamondCutFacet provides the core functionality for upgrading and managing the functions within a Compose diamond. It allows adding, replacing, and removing facet functions, enabling dynamic contract logic updates and feature extensibility. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - ---- -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond/facets/DiamondCutFacet.sol"; - -contract DiamondDeployer { - // Assume diamond is already deployed and initialized - IDiamondCut immutable diamondCutFacet; - - constructor(address _diamondAddress) { - diamondCutFacet = IDiamondCut(_diamondAddress); - } - - function upgradeDiamond() external { - // Example: Add a new function - // address newFacetAddress = address(new MyNewFacet()); - // bytes32[] memory selectors = new bytes32[](1); - // selectors[0] = IDiamondCut.myNewFunction.selector; - // diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](0), newFacetAddress); - - // Example: Replace an existing function - // address updatedFacetAddress = address(new MyUpdatedFacet()); - // bytes32[] memory selectorsToReplace = new bytes32[](1); - // selectorsToReplace[0] = IDiamondCut.existingFunction.selector; - // diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](1), new IDiamondCut.FacetCut[](0), updatedFacetAddress); - - // Example: Remove a function - // bytes32[] memory selectorsToRemove = new bytes32[](1); - // selectorsToRemove[0] = IDiamondCut.deprecatedFunction.selector; - // diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](0), new IDiamondCut.FacetCut[](1), address(0)); // address(0) signifies removal - - // To execute a function after the cut, pass it in the last argument - // diamondCutFacet.diamondCut(..., abi.encodeCall(IDiamondCut.someFunction, (arg1, arg2))); - } -}`} - - -## Best Practices - - -- Ensure the `diamondCut` function is only callable by authorized addresses (e.g., an owner or a governance contract) to prevent unauthorized upgrades. -- Carefully manage the mapping of function selectors to facet addresses during upgrades to avoid breaking existing functionality or introducing vulnerabilities. -- When replacing or removing functions, consider the impact on dependent facets and external contracts interacting with the diamond. - - -## Security Considerations - - -Access to `diamondCut`, `addFunctions`, `replaceFunctions`, and `removeFunctions` must be strictly controlled. Unauthorized calls can lead to the diamond's functionality being compromised. Ensure that the `diamondCut` function is not susceptible to reentrancy if it includes an optional `delegatecall`. Input validation on function selectors and facet addresses is crucial. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx deleted file mode 100644 index 70eebdea..00000000 --- a/website/docs/contracts/facets/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,255 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "The functions in DiamondLoupeFacet MUST be added to a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -The functions in DiamondLoupeFacet MUST be added to a diamond. - - - -- Provides comprehensive read-only access to the diamond's facet registry. -- Optimized for gas efficiency, especially in diamonds with numerous facets and selectors. -- Enables dynamic discovery of diamond functionality without prior knowledge of facet addresses. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows querying facet addresses, associated function selectors, and the overall facet structure of the diamond, enabling builders to understand and interact with the diamond's deployed functionality. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondLoupeFacet} from "@compose/diamond-contracts/contracts/facets/DiamondLoupeFacet.sol"; - -contract DiamondLoupeConsumer { - IDiamondLoupeFacet diamondLoupeFacet; - - constructor(address _diamondAddress) { - diamondLoupeFacet = IDiamondLoupeFacet(_diamondAddress); - } - - function getAllFacets() public view returns (IDiamondLoupeFacet.Facet[] memory) { - return diamondLoupeFacet.facets(); - } - - function getFacetAddress(bytes4 _selector) public view returns (address) { - return diamondLoupeFacet.facetAddress(_selector); - } - - function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { - return diamondLoupeFacet.facetFunctionSelectors(_facet); - } -}`} - - -## Best Practices - - -- Integrate `DiamondLoupeFacet` into your diamond to provide necessary introspection for developers and external tools. -- Call loupe functions with `view` or `pure` modifiers to avoid unnecessary gas costs. -- Cache frequently accessed loupe data in your own contracts if performance becomes a bottleneck, though direct calls are generally gas-efficient. - - -## Security Considerations - - -The `DiamondLoupeFacet` is a read-only facet. Its functions do not modify state and are generally considered safe. Ensure that the diamond's storage (specifically `s.selectors` and `s.facetAndPosition`) is managed securely by authorized facets, as the loupe facet's output is directly dependent on this underlying state. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx deleted file mode 100644 index 60e3cb4f..00000000 --- a/website/docs/contracts/facets/ERC1155Facet.mdx +++ /dev/null @@ -1,671 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements the ERC-1155 standard for fungible and non-fungible tokens. -- Supports batched operations for transfers and balance checks, improving efficiency. -- Provides flexible URI resolution for token metadata. - - -## Overview - -The ERC1155Facet provides a standard implementation for the ERC-1155 Multi-Token Standard within a Compose diamond. It manages token balances, approvals, and URI resolution, enabling the diamond to act as a versatile token issuer and manager. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `&#123;id&#125;` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; -import {ERC1155Facet} from "@compose/contracts/src/facets/ERC1155/ERC1155Facet.sol"; - -contract ERC1155DiamondConsumer { - address public diamondAddress; - - // Assume diamondAddress is set during deployment - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getTokenBalance(uint256 _id, address _account) external view returns (uint256) { - // Get the ERC1155Facet interface - IERC1155Facet erc1155Facet = IERC1155Facet(diamondAddress); - - // Call the balanceOf function on the diamond proxy - return erc1155Facet.balanceOf(_account, _id); - } - - function getTokenURI(uint256 _id) external view returns (string memory) { - IERC1155Facet erc1155Facet = IERC1155Facet(diamondAddress); - return erc1155Facet.uri(_id); - } -}`} - - -## Best Practices - - -- Initialize the ERC1155Facet with a dedicated storage slot during diamond deployment. -- Ensure appropriate access control is implemented at the diamond level for functions like `setApprovalForAll` if restricted operation is desired. -- When implementing custom token URIs, carefully manage the `baseURI` and `tokenURIs` storage to ensure correct resolution. - - -## Security Considerations - - -Access control for `setApprovalForAll` should be considered; by default, it's permissionless. Input validation for token IDs and amounts is handled by the facet, but downstream logic should also validate. Reentrancy is not a direct concern for most ERC1155 operations, but custom logic interacting with token transfers should be audited. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx deleted file mode 100644 index b2f5c9ec..00000000 --- a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,417 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Enables secure cross-chain minting and burning of ERC20 tokens. -- Enforces `trusted-bridge` role for critical cross-chain operations. -- Provides internal mechanisms to access facet-specific storage directly. - - -## Overview - -The ERC20BridgeableFacet manages cross-chain minting and burning operations for ERC20 tokens within a Compose diamond. It enforces access control for trusted bridge operators and provides internal utility functions for interacting with diamond storage and verifying bridge permissions. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableFacet} from "@compose-protocol/diamond-contracts/contracts/facets/erc20/ERC20BridgeableFacet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; - -contract Deployer { - function deploy() public { - // Assume diamondDeployer is an instance of a contract that handles diamond deployment - // and has already deployed the diamond contract and its facets. - // The ERC20BridgeableFacet is assumed to be cut into the diamond at a specific address. - address diamondAddress = address(0xYourDiamondAddress); - IERC20BridgeableFacet erc20BridgeableFacet = IERC20BridgeableFacet(diamondAddress); - - // Example of cross-chain minting (requires trusted-bridge role) - // address to = address(0xRecipientAddress); - // uint256 amount = 1000 ether; - // address tokenAddress = address(0xERC20TokenAddress); - // erc20BridgeableFacet.crosschainMint(to, amount, tokenAddress); - - // Example of cross-chain burning (requires trusted-bridge role) - // erc20BridgeableFacet.crosschainBurn(address(0xSenderAddress), 500 ether, address(0xERC20TokenAddress)); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is correctly assigned within the diamond's AccessControl facet for authorized cross-chain operations. -- Utilize the `getERC20Storage` and `getAccessControlStorage` functions for internal logic that requires direct access to facet storage, ensuring correct slot referencing via assembly. -- When upgrading, ensure the storage layout compatibility is maintained if new state variables are introduced in future versions of this facet. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are protected by an access control check that requires the caller to possess the `trusted-bridge` role. The `checkTokenBridge` internal function explicitly validates this role and prevents zero-address callers. There is no explicit reentrancy guard; callers must ensure that the operations triggered by minting or burning do not introduce reentrancy vulnerabilities. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx deleted file mode 100644 index fce59584..00000000 --- a/website/docs/contracts/facets/ERC20BurnFacet.mdx +++ /dev/null @@ -1,276 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "Contract documentation for ERC20BurnFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ERC20BurnFacet - - - -- Allows for the reduction of total token supply by destroying tokens. -- Supports burning tokens directly from the caller's balance. -- Enables burning tokens from another account, contingent on an approved allowance. - - -## Overview - -The ERC20BurnFacet provides functionality to destroy ERC20 tokens within a Compose diamond. It enables users to burn their own tokens or burn tokens from other accounts if an allowance is set, facilitating token supply reduction and management. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-solidity/src/diamond/IDiamondCut.sol"; -import {IERC20BurnFacet} from "@compose/diamond-solidity/src/facets/erc20/IERC20BurnFacet.sol"; - -contract Deployer { - address diamondAddress; - - function deploy() public { - // ... deployment logic to create the diamond proxy ... - diamondAddress = address(0xYourDiamondProxyAddress); - - // Add ERC20BurnFacet - address erc20BurnFacetAddress = address(new ERC20BurnFacet()); - bytes32[] memory selectors = new bytes32[](3); - selectors[0] = IERC20BurnFacet.getStorage.selector; - selectors[1] = IERC20BurnFacet.burn.selector; - selectors[2] = IERC20BurnFacet.burnFrom.selector; - - bytes[] memory functionSignatures = new bytes[](3); - functionSignatures[0] = abi.encodeWithSignature("getStorage() returns (ERC20Storage storage)"); - functionSignatures[1] = abi.encodeWithSignature("burn(uint256 _amount)"); - functionSignatures[2] = abi.encodeWithSignature("burnFrom(address _from, uint256 _amount)"); - - IDiamondCut(diamondAddress).diamondCut( - IDiamondCut.FacetCut[] - ( - { - facetAddress: erc20BurnFacetAddress, - action: IDiamondCut.Action.ADD, - functionSelectors: selectors - } - ), - address(0), - "" - ); - } - - function burnMyTokens() public { - IERC20BurnFacet(diamondAddress).burn(100 * 1 ether); - } - - function burnAllowanceTokens(address _spender, uint256 _amount) public { - IERC20BurnFacet(diamondAddress).burnFrom(_spender, _amount); - } -}`} - - -## Best Practices - - -- Initialize the ERC20BurnFacet within your diamond's deployment script, ensuring the correct selectors are mapped. -- When calling `burnFrom`, ensure an allowance has been previously set for the caller by the `_from` address. -- The `burn` and `burnFrom` functions correctly emit a `Transfer` event to the zero address, signaling token destruction. - - -## Security Considerations - - -The `burnFrom` function relies on the ERC20 `allowance` mechanism. Ensure that the `allowance` is managed securely and that users understand the implications of granting allowances. No reentrancy risks are present as the functions do not make external calls. Input validation for `_amount` should be handled by the caller or the ERC20 storage logic to prevent underflows or overflows. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx deleted file mode 100644 index 4d0d3ba0..00000000 --- a/website/docs/contracts/facets/ERC20Facet.mdx +++ /dev/null @@ -1,594 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "Contract documentation for ERC20Facet" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ERC20Facet - - - -- Full ERC20 token functionality including name, symbol, decimals, supply, balances, and allowances. -- Supports standard token transfer and approval operations. -- Integrates seamlessly with the Compose diamond proxy pattern, allowing it to be composed with other facets. - - -## Overview - -The ERC20Facet implements the ERC20 token standard on a Compose diamond. It provides standard functions for managing token supply, balances, allowances, and facilitating token transfers. This facet enables a diamond to act as a compliant ERC20 token, allowing for fungible asset management within the diamond's ecosystem. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Facet} from "@compose/diamond/facets/ERC20/IERC20Facet.sol"; -import {IDiamondLoupe} from "@compose/diamond/facets/DiamondLoupe/IDiamondLoupe.sol"; - -contract ERC20Consumer { - IERC20Facet private erc20Facet; - - function initialize(address _diamondAddress) external { - // Assume the diamond is already deployed and has the ERC20Facet functions - // attached to it. This is typically done during diamond deployment. - erc20Facet = IERC20Facet(_diamondAddress); - } - - function getTokenName() external view returns (string memory) { - return erc20Facet.name(); - } - - function getTokenSymbol() external view returns (string memory) { - return erc20Facet.symbol(); - } - - function getTokenDecimals() external view returns (uint8) { - return erc20Facet.decimals(); - } - - function getTotalSupply() external view returns (uint256) { - return erc20Facet.totalSupply(); - } - - function getBalanceOf(address _account) external view returns (uint256) { - return erc20Facet.balanceOf(_account); - } - - function getAllowance(address _owner, address _spender) external view returns (uint256) { - return erc20Facet.allowance(_owner, _spender); - } - - function approve(address _spender, uint256 _amount) external { - erc20Facet.approve(_spender, _amount); - } - - function transfer(address _recipient, uint256 _amount) external { - erc20Facet.transfer(_recipient, _amount); - } - - function transferFrom(address _sender, address _recipient, uint256 _amount) external { - erc20Facet.transferFrom(_sender, _recipient, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC20Facet is correctly initialized with the diamond's storage layout during deployment. -- Implement access control mechanisms for functions like `approve`, `transfer`, and `transferFrom` if specific roles or permissions are required beyond standard ERC20 behavior. -- When upgrading, ensure the storage slot for ERC20 state remains consistent to prevent data loss or corruption. - - -## Security Considerations - - -Standard ERC20 reentrancy considerations apply to `transfer` and `transferFrom`. Ensure that any custom logic interacting with these functions or emitted events is reentrancy-safe. Input validation for amounts and addresses should be handled by the facet itself to prevent unexpected behavior. Access to `approve` and `transferFrom` should be managed carefully if specific permissioning is layered on top of the standard ERC20 logic. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx deleted file mode 100644 index 535b8fc5..00000000 --- a/website/docs/contracts/facets/ERC20PermitFacet.mdx +++ /dev/null @@ -1,337 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "Contract documentation for ERC20PermitFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ERC20PermitFacet - - - -- Implements EIP-2612 permit functionality for gas-efficient token approvals. -- Provides `nonces` to track and prevent replay attacks for permits. -- Exposes `DOMAIN_SEPARATOR` for correct signature verification against the specific diamond instance and chain ID. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant ERC20 token approvals via off-chain signatures. It integrates with the diamond proxy to manage token permits, allowing users to grant allowances without direct on-chain transactions for each approval. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - ---- -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IDiamondCut} from "../diamond/IDiamond.sol"; -import {ERC20PermitFacet} from "./ERC20PermitFacet.sol"; - -contract Deployer { - function deploy() external { - // Assume diamond is deployed and facets are registered - address diamond = address(0x123); // Replace with actual diamond address - - ERC20PermitFacet permitFacet = new ERC20PermitFacet(); - - IDiamondCut(diamond).diamondCut( - new IDiamondCut.FacetCut[]( - {facetAddress: address(permitFacet), action: IDiamondCut.FacetCutAction.ADD, selectors: IDiamondCut.getSelectors(permitFacet)} - ), - address(0), - "" - ); - - // To use permit: - // 1. Get nonce: ERC20PermitFacet(diamond).nonces(owner) - // 2. Get domain separator: ERC20PermitFacet(diamond).DOMAIN_SEPARATOR() - // 3. Construct permit data and sign off-chain - // 4. Call permit: - // ERC20PermitFacet(diamond).permit(owner, spender, value, deadline, v, r, s); - } -}`} - - -## Best Practices - - -- Initialize the facet during diamond deployment or upgrade, ensuring the `DOMAIN_SEPARATOR` is correctly configured for the deployed chain. -- Use the `nonces` and `DOMAIN_SEPARATOR` functions to construct valid permit data off-chain before calling the `permit` function. -- Ensure the owner of the token correctly signs the permit message to prevent unauthorized approvals. - - -## Security Considerations - - -The `permit` function relies on off-chain signatures. Ensure the signature verification process is robust and that the `owner` address signing the permit has sufficient balance and is the intended grantor of the allowance. The `nonces` mapping prevents replay attacks; it's crucial that this mapping is managed correctly and incremented upon successful permit usage. The `DOMAIN_SEPARATOR` ensures that signatures are chain-specific, preventing cross-chain replay attacks. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx deleted file mode 100644 index 12b3c978..00000000 --- a/website/docs/contracts/facets/ERC6909Facet.mdx +++ /dev/null @@ -1,529 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements the ERC-6909 standard for flexible token management. -- Supports both fungible and non-fungible token semantics through token IDs. -- Provides explicit functions for balance checks, allowances, and operator approvals. -- Enables direct token transfers via `transfer` and `transferFrom`. - - -## Overview - -The ERC6909Facet implements the ERC-6909 standard for fungible and non-fungible tokens within a Compose diamond. It provides core functionalities for managing token balances, allowances, operator approvals, and executing transfers, acting as a primary interface for token interactions. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Facet} from "@compose/contracts/interfaces/token/ERC6909/IERC6909Facet.sol"; -import {IERC6909Storage} from "@compose/contracts/interfaces/token/ERC6909/IERC6909Storage.sol"; - -contract ERC6909Consumer { - // Assume diamondAbi is the ABI of your diamond proxy - // Assume diamondAddress is the address of your diamond proxy - IERC6909Facet public erc6909Facet; - - constructor(address diamondAddress) { - erc6909Facet = IERC6909Facet(diamondAddress); - } - - function consumeERC6909() external { - // Example: Get balance - uint256 balance = erc6909Facet.balanceOf(address(this), 1); // For token ID 1 - - // Example: Approve allowance - erc6909Facet.approve(msg.sender, 1, 100); // Approve 100 units of token ID 1 - - // Example: Transfer tokens - erc6909Facet.transfer(msg.sender, address(this), 1, 50); // Transfer 50 units of token ID 1 - } -}`} - - -## Best Practices - - -- Initialize the ERC6909 storage correctly during diamond deployment or upgrade. The `getStorage` function provides access to the storage slot. -- Carefully manage access control for functions like `setOperator` and `approve` to prevent unauthorized actions. -- Ensure token IDs and amounts are validated before calling transfer or approve functions. - - -## Security Considerations - - -The `transfer` and `transferFrom` functions should be carefully audited for reentrancy risks if they interact with external contracts. Ensure proper input validation for token IDs, amounts, sender, and receiver addresses to prevent unexpected behavior or exploits. Access control for `approve` and `setOperator` is critical to prevent unauthorized token management. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx deleted file mode 100644 index 3f7a0a06..00000000 --- a/website/docs/contracts/facets/ERC721BurnFacet.mdx +++ /dev/null @@ -1,221 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Enables the irreversible destruction of ERC-721 tokens. -- Integrates seamlessly with the Compose diamond storage pattern. -- Provides a clear interface for token burning operations. - - -## Overview - -The ERC721BurnFacet provides the functionality to burn (destroy) ERC-721 tokens within a Compose diamond. It allows for the removal of tokens from tracking and enumeration, reducing the total supply and freeing up associated metadata. This facet interacts directly with the diamond's storage to manage token states. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "@compose/contracts/src/facets/erc721/ERC721BurnFacet.sol"; -import {IDiamondCut} from "@compose/contracts/src/interfaces/IDiamondCut.sol"; - -contract Deployer { - address constant ERC721_BURN_FACET_ADDRESS = address(0xABC); // Replace with actual facet address - address constant DIAMOND_ADDRESS = address(0xDEF); // Replace with actual diamond address - - function deploy() external { - IDiamondCut(DIAMOND_ADDRESS).diamondCut( - IDiamondCut.FacetCut[]( - (IDiamondCut.FacetCut({ - facetAddress: ERC721_BURN_FACET_ADDRESS, - action: IDiamondCut.FacetCutAction.ADD, - selectors: - bytes4(keccak256("burn(uint256)")) | - bytes4(keccak256("getStorage()\0x1a2b3c4d")) // Example selector, replace with actual - })) - ), - address(0), // Initialize facetAddresses (optional) - bytes("") // Initialize facetCalldata (optional) - ); - } - - function burnToken(uint256 tokenId) external { - IERC721BurnFacet(DIAMOND_ADDRESS).burn(tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721BurnFacet` is added to the diamond with the correct selectors during deployment or upgrade. -- Implement proper access control mechanisms within your diamond's governance to restrict who can call the `burn` function, if necessary. -- Understand that burning a token is irreversible; ensure user intent is validated before execution. - - -## Security Considerations - - -The `burn` function should be protected by appropriate access control to prevent unauthorized token destruction. The facet directly manipulates internal storage, so ensure its integration does not conflict with other facets managing token states or ownership. Reentrancy is not a concern as the function does not make external calls. Input validation on `tokenId` is crucial to prevent unexpected behavior. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index 16d0b86f..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,224 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Enables burning of ERC721 tokens, effectively destroying them. -- Integrates with enumeration tracking to maintain accurate token lists after burning. - - -## Overview - -The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens while ensuring proper removal from enumeration tracking. It exposes a method to retrieve its internal storage and a dedicated burn function. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableBurnFacet} from "@compose/contracts/src/facets/ERC721/ERC721EnumerableBurnFacet.sol"; - -contract ERC721EnumerableBurnFacetConsumer { - IERC721EnumerableBurnFacet public immutable erc721EnumerableBurnFacet; - - constructor(address _diamondAddress) { - erc721EnumerableBurnFacet = IERC721EnumerableBurnFacet(_diamondAddress); - } - - function burnToken(uint256 _tokenId) public { - erc721EnumerableBurnFacet.burn(_tokenId); - } - - function getFacetStorage() public view returns (IERC721EnumerableBurnFacet.ERC721EnumerableBurnStorage memory) { - return erc721EnumerableBurnFacet.getStorage(); - } -}`} - - -## Best Practices - - -- Ensure the facet is properly initialized within the diamond proxy contract. -- Grant appropriate access control to the `burn` function if it's not intended to be permissionless. - - -## Security Considerations - - -The `burn` function should be protected by appropriate access control mechanisms to prevent unauthorized token destruction. Ensure that the caller has the necessary permissions to burn the specified token. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx deleted file mode 100644 index 7ea55781..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,743 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements the full ERC721 standard, including enumerable extensions for tracking token counts and ownership by index. -- Provides essential NFT metadata functions: `name()`, `symbol()`, and `tokenURI()`. -- Supports approval mechanisms for single tokens (`approve`) and bulk approvals (`setApprovalForAll`). - - -## Overview - -The ERC721EnumerableFacet implements the ERC721 standard with enumerable extensions, providing core NFT functionalities like name, symbol, token URI, and ownership management. It also includes essential functions for tracking total supply, balance, owner of specific tokens, and approvals, facilitating comprehensive NFT operations within a Compose diamond. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableFacet} from \"@compose-protocol/diamond/contracts/facets/ERC721/IERC721EnumerableFacet.sol\"; - -contract ERC721EnumerableConsumer { - IERC721EnumerableFacet private immutable erc721Facet; - - constructor(address _diamondAddress) { - erc721Facet = IERC721EnumerableFacet(_diamondAddress); - } - - function getTokenName() external view returns (string memory) { - return erc721Facet.name(); - } - - function getTokenSymbol() external view returns (string memory) { - return erc721Facet.symbol(); - } - - function getTotalSupply() external view returns (uint256) { - return erc721Facet.totalSupply(); - } - - function getBalance(address _owner) external view returns (uint256) { - return erc721Facet.balanceOf(_owner); - } - - function getOwnerOf(uint256 _tokenId) external view returns (address) { - return erc721Facet.ownerOf(_tokenId); - } -}`} - - -## Best Practices - - -- Initialize the ERC721EnumerableFacet within the diamond's deployment process, ensuring all required storage is correctly set up. -- When transferring tokens, prefer `safeTransferFrom` over `transferFrom` to ensure receiver compatibility if the receiver is a contract. -- Access control for functions like `approve` and `setApprovalForAll` is handled implicitly by the ERC721 standard; ensure correct ownership checks are performed by the caller. - - -## Security Considerations - - -The `safeTransferFrom` functions include checks to prevent reentrancy and ensure the receiving contract can handle ERC721 tokens. Standard ERC721 ownership checks are inherent to functions like `transferFrom`, `approve`, and `ownerOf`. Users must ensure they are interacting with the correct diamond address and that the caller has the necessary permissions for state-modifying functions. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx deleted file mode 100644 index 74f59fee..00000000 --- a/website/docs/contracts/facets/ERC721Facet.mdx +++ /dev/null @@ -1,669 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Full ERC-721 compliance. -- Support for token metadata via `tokenURI`. -- Internal transfer logic for robust state management. -- Approval mechanisms for token transfers. - - -## Overview - -The ERC721Facet provides a full implementation of the ERC-721 Non-Fungible Token Standard within a Compose diamond. It handles token creation, transfers, approvals, and metadata retrieval, acting as the primary interface for ERC-721 compliant collections. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose/contracts/interfaces/IERC721Facet.sol"; -import {DiamondProxy} from "@compose/contracts/DiamondProxy.sol"; - -contract ERC721Deployer { - IERC721Facet public erc721Facet; - - function deployERC721(address diamondProxyAddress) external { - erc721Facet = IERC721Facet(diamondProxyAddress); - // Assume ERC721Facet has been added to the diamond. - } - - function getTokenName() external view returns (string memory) { - return erc721Facet.name(); - } - - function getTokenSymbol() external view returns (string memory) { - return erc721Facet.symbol(); - } - - function getTokenOwner(uint256 tokenId) external view returns (address) { - return erc721Facet.ownerOf(tokenId); - } - - function transferToken(address to, uint256 tokenId) external { - // Ensure caller is approved or owner - erc721Facet.transferFrom(msg.sender, to, tokenId); - } -}`} - - -## Best Practices - - -- Initialize the ERC721Facet with a unique name and symbol during diamond deployment. -- Utilize `safeTransferFrom` for transfers to ensure receiver contract compatibility. -- Manage approvals carefully, especially for `setApprovalForAll`, to prevent unintended asset access. - - -## Security Considerations - - -The `transferFrom` and `safeTransferFrom` functions require careful access control to ensure only the token owner or an approved address can initiate a transfer. The `setApprovalForAll` function should be used with caution as it grants broad permissions. Reentrancy is mitigated by using Checks-Effects-Interactions pattern within transfer functions. Input validation is performed on token IDs and addresses. - - -
- -
- - diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx deleted file mode 100644 index 0199a7b8..00000000 --- a/website/docs/contracts/facets/ExampleDiamond.mdx +++ /dev/null @@ -1,146 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Contract documentation for ExampleDiamond" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ExampleDiamond - - - -- Manages function selector to facet address mapping for routing. -- Initializes the diamond with an owner and an initial set of facets. -- Supports adding, replacing, and removing facets via its constructor's `FacetCut` structure. - - -## Overview - -The ExampleDiamond contract serves as the core of a Compose diamond proxy. It orchestrates facet registration and routing, acting as the primary interface for interacting with various diamond modules. Its constructor is crucial for initial setup, mapping function selectors to facet addresses. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut &#123; address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; &#125; Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ExampleDiamond} from "./ExampleDiamond.sol"; -import {SomeFacet} from "./SomeFacet.sol"; - -contract DeployDiamond { - // Define facet cuts - struct FacetCut { - address facetAddress; - uint8 action; // Add=0, Replace=1, Remove=2 - bytes4[] functionSelectors; - } - - function deploy() public { - // Example facet data - address someFacetAddress = address(new SomeFacet()); - bytes4[] memory someFacetSelectors = new bytes4[](1); - someFacetSelectors[0] = SomeFacet.someFunction.selector; // Assuming SomeFacet has someFunction - - FacetCut[] memory cuts = new FacetCut[](1); - cuts[0] = FacetCut({ - facetAddress: someFacetAddress, - action: 0, // Add - functionSelectors: someFacetSelectors - }); - - // Deploy ExampleDiamond and initialize - ExampleDiamond diamond = new ExampleDiamond(); - // The constructor of ExampleDiamond is called implicitly here, - // but for explicit initialization of facets, a separate function - // like \`diamondCut\` would typically be called after deployment. - // The provided ExampleDiamond's constructor directly takes facet data. - - // For demonstration, assume the constructor takes these cuts directly: - // ExampleDiamond diamond = new ExampleDiamond(cuts, msg.sender); - - // In a real scenario, you would call \`diamondCut\` on the deployed diamond proxy. - // diamond.diamondCut(cuts, address(0), ""); - } -}`} - - -## Best Practices - - -- Initialize the diamond with all necessary facets and their function selectors during deployment using the constructor. -- Ensure the owner is set correctly during initialization for future upgradeability. -- Understand that the `constructor` directly registers facets; subsequent upgrades would use a `diamondCut` function (not shown in the provided contract). - - -## Security Considerations - - -The `constructor` is a critical setup function. Ensure the `facetAddress` values provided are verified and the `functionSelectors` accurately reflect the functions intended to be exposed by each facet. Ownership transfer should be handled carefully after initialization to maintain control. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx deleted file mode 100644 index 7809a9b0..00000000 --- a/website/docs/contracts/facets/OwnerFacet.mdx +++ /dev/null @@ -1,212 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Manages the single owner address for the diamond contract. -- Supports transferring ownership to a new address. -- Allows for complete renunciation of ownership. - - -## Overview - -The OwnerFacet provides essential ownership management for a Compose diamond. It allows for retrieving the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative actions and ensuring secure contract governance. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose-protocol/diamond/contracts/facets/Owner/IOwnerFacet.sol"; - -contract OwnerConsumer { - IOwnerFacet private immutable _ownerFacet; - - constructor(address _diamondAddress) { - _ownerFacet = IOwnerFacet(_diamondAddress); - } - - function getDiamondOwner() external view returns (address) { - return _ownerFacet.owner(); - } - - function transferDiamondOwnership(address _newOwner) external { - _ownerFacet.transferOwnership(_newOwner); - } - - function renounceDiamondOwnership() external { - _ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize the diamond with the OwnerFacet to establish initial ownership. -- Use `transferOwnership` for controlled transitions of administrative control. -- Ensure the `owner` address has appropriate permissions for critical diamond functions. - - -## Security Considerations - - -The `transferOwnership` function can be used to set the new owner to `address(0)`, effectively renouncing ownership. This action is irreversible. Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx deleted file mode 100644 index e995de62..00000000 --- a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,287 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Secure two-step ownership transfer mechanism. -- Explicit `acceptOwnership` call prevents accidental ownership changes. -- Functions `owner()`, `pendingOwner()`, `transferOwnership()`, `acceptOwnership()`, and `renounceOwnership()` provide full ownership management capabilities. - - -## Overview - -The OwnerTwoStepsFacet manages contract ownership through a two-step verification process. It allows the current owner to initiate a transfer to a new owner, who must then explicitly accept the ownership, enhancing security against accidental or malicious ownership changes. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - ---- -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "../interfaces/IOwnerTwoStepsFacet.sol"; - -contract OwnerDeployer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function transferOwnershipToNewOwner(address _newOwner) public { - IOwnerTwoStepsFacet(diamondAddress).transferOwnership(_newOwner); - } - - function acceptOwnershipFromCurrentOwner() public { - IOwnerTwoStepsFacet(diamondAddress).acceptOwnership(); - } - - function renounceContractOwnership() public { - IOwnerTwoStepsFacet(diamondAddress).renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize ownership by calling `transferOwnership` with the desired address, followed by the new owner calling `acceptOwnership`. -- Use `owner()` and `pendingOwner()` to track the current and awaiting owner. -- Store `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` constants within your diamond deployment scripts or configuration for consistent slot access. - - -## Security Considerations - - -Ownership transfer functions (`transferOwnership`, `acceptOwnership`, `renounceOwnership`) are typically protected by access control mechanisms within the diamond's governance framework. Ensure that only authorized entities can call these functions. `transferOwnership` sets a pending owner, which is only updated to the actual owner upon a successful `acceptOwnership` call by the pending owner. Direct access to storage slots via `getOwnerStorage` and `getPendingOwnerStorage` requires careful handling to avoid unintended state modifications. - - -
- -
- - diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx deleted file mode 100644 index 6f9a48ff..00000000 --- a/website/docs/contracts/facets/RoyaltyFacet.mdx +++ /dev/null @@ -1,211 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements the `royaltyInfo` function as per ERC-2981. -- Supports dynamic configuration of token-specific royalties on top of a default royalty rate. -- Utilizes inline assembly for efficient access to storage, minimizing gas costs. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard for on-chain royalty payments. It allows for querying royalty information for a given token and sale price, supporting both token-specific and default royalty configurations. This facet centralizes royalty logic within the diamond, providing a consistent interface for marketplaces and other integrators. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; -import {IRoyaltyFacet} from "@compose/diamond/contracts/facets/RoyaltyFacet/IRoyaltyFacet.sol"; - -contract Deployer { - address public diamondAddress; - - function deployDiamond() public { - // Assume diamond implementation and facets are already deployed - // ... - address royaltyFacetAddress = address(new RoyaltyFacet()); // Example address - - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: royaltyFacetAddress, - action: IDiamondCut.FacetCutAction.ADD, - selectors: - new bytes4[](3) // Including getStorage, royaltyInfo, and any internal selectors if exposed - }); - // Set selectors for royaltyInfo and getStorage - cuts[0].selectors[0] = IRoyaltyFacet.royaltyInfo.selector; - cuts[0].selectors[1] = IRoyaltyFacet.getStorage.selector; - - // Assume diamond init contract is deployed and has the diamondCut function - // DiamondInit(diamondInitAddress).diamondCut(cuts, address(0), ""); - // diamondAddress = address(this); // Or wherever the diamond is deployed - } - - function getRoyaltyInfo(address _diamondAddress, uint256 _tokenId, uint256 _salePrice) public view returns (address, uint256) { - // Call royaltyInfo via the diamond proxy - // The diamond proxy will route the call to the RoyaltyFacet - bytes4 selector = IRoyaltyFacet.royaltyInfo.selector; - (bool success, bytes memory data) = _diamondAddress.call(abi.encodeWithSelector(selector, _tokenId, _salePrice)); - require(success, "Royalty query failed"); - return (abi.decode(data, (address, uint256))); - } -}`} - - -## Best Practices - - -- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. -- Ensure marketplaces or other integrators call the `royaltyInfo` function through the diamond proxy address. -- Store the `STORAGE_POSITION` for `RoyaltyStorage` in a well-known, immutable location accessible to all facets that might interact with royalty data. - - -## Security Considerations - - -The `royaltyInfo` function calculates royalties as a percentage of the sale price. Ensure that the basis points for royalties are validated to prevent excessive or zero royalty amounts. Access control is managed by the diamond proxy; ensure that only authorized entities can set or modify default/token-specific royalties if such administrative functions are implemented in other facets. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx deleted file mode 100644 index af06c461..00000000 --- a/website/docs/contracts/modules/AccessControlMod.mdx +++ /dev/null @@ -1,447 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Emitted when the admin role for a role is changed." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Emitted when the admin role for a role is changed. - - - -- Role-based access control: Define and manage distinct roles with granular permissions. -- Permission management functions: `grantRole`, `revokeRole`, and `setRoleAdmin` provide flexible control over role assignments and administration. -- Explicit role checking: `hasRole` and `requireRole` allow for clear and auditable permission checks within facets. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlMod module provides a robust role-based access control system for Compose diamonds. It enables granular permission management by allowing roles to be granted, revoked, and checked, ensuring that only authorized accounts can perform sensitive operations. This is crucial for maintaining the security and integrity of diamond applications. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. **Returns** - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. **Parameters** **Returns** - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. **Parameters** **Returns** - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. **Note:** error: AccessControlUnauthorizedAccount If the account does not have the role. **Parameters** - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. **Parameters** **Returns** - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. **Parameters** - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. **Parameters** -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. **Parameters** -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. **Parameters** -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. **Parameters** -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControl} from "@compose/diamond-contracts/contracts/modules/access/IAccessControlMod.sol"; - -contract AccessControlUserFacet { - IAccessControl internal accessControl; - - // Assume accessControl is initialized in an initializer function - // and points to the AccessControlMod facet address. - - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - - function grantMinterRole(address _account) external { - // Ensure the caller has the authority to grant roles - accessControl.requireRole(_account, ADMIN_ROLE); - accessControl.grantRole(MINTER_ROLE, _account); - } - - function hasMinterRole(address _account) external view returns (bool) { - return accessControl.hasRole(MINTER_ROLE, _account); - } -}`} - - -## Best Practices - - -- Always use `requireRole` for internal checks within facets to enforce access control before executing critical logic. -- Manage role administration carefully. Only grant the `ADMIN_ROLE` to trusted accounts, and use `setRoleAdmin` to define clear administrative hierarchies for other roles. -- When upgrading the diamond, ensure the AccessControlMod facet is properly updated or re-initialized if its storage layout changes, to maintain consistent access control. - - -## Integration Notes - - -The AccessControlMod module manages its state within the diamond's storage. Facets interact with it through the `IAccessControl` interface. Changes to roles and role admins made via the AccessControlMod functions are immediately reflected and visible to all facets interacting with the module. Ensure the `AccessControlMod` facet is deployed and its address is correctly registered in the diamond's facet registry for other facets to interact with it. The module relies on the diamond's storage pattern for state persistence. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx deleted file mode 100644 index f62a69b6..00000000 --- a/website/docs/contracts/modules/AccessControlPausableMod.mdx +++ /dev/null @@ -1,384 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Event emitted when a role is paused." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Event emitted when a role is paused. - - - -- Granular role pausing: Allows individual roles to be paused independently. -- Role-specific checks: `requireRoleNotPaused` enforces both role ownership and active status. -- Access control storage access: Provides functions to retrieve underlying storage structures for inspection. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlPausable module provides role-based access control with the ability to pause specific roles. This enables granular control over sensitive operations, allowing administrators to temporarily halt functionality associated with certain roles without affecting the entire system. It enhances diamond safety by offering a mechanism to mitigate risks during emergencies or upgrades. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. **Returns** - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. **Returns** - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. **Parameters** **Returns** - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. **Parameters** - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRolePaused If the role is paused. **Parameters** - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. **Parameters** - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. **Parameters** -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. **Parameters** -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. **Parameters** -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. **Parameters** -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausable} from "@compose/modules/access-control/IAccessControlPausable.sol"; - -contract MyFacet { - IAccessControlPausable public constant accessControlPausable = IAccessControlPausable(address(this)); // Replace with actual diamond proxy address - - address public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - - /** - * @notice Pauses the ADMIN_ROLE. - */ - function pauseAdminRole() external { - accessControlPausable.pauseRole(ADMIN_ROLE); - } - - /** - * @notice Unpauses the ADMIN_ROLE. - */ - function unpauseAdminRole() external { - accessControlPausable.unpauseRole(ADMIN_ROLE); - } - - /** - * @notice Requires that the caller has the ADMIN_ROLE and that the role is not paused. - */ - function sensitiveOperation() external { - accessControlPausable.requireRoleNotPaused(msg.sender, ADMIN_ROLE); - // ... perform sensitive operation ... - } -}`} - - -## Best Practices - - -- Ensure that roles intended to be paused are properly managed and that only authorized entities can call `pauseRole` and `unpauseRole`. -- Utilize `requireRoleNotPaused` before executing critical functions to prevent unauthorized actions when a role is paused. -- Understand that pausing a role affects all accounts assigned to it; consider the implications before pausing. - - -## Integration Notes - - -This module interacts with the diamond's storage to manage role pausing states. Facets can call the public functions of this module directly via the diamond proxy. The `AccessControlPausable` storage is distinct from the base `AccessControl` storage. Changes to role pausing are immediately reflected across all facets interacting with the module. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx deleted file mode 100644 index faa2d976..00000000 --- a/website/docs/contracts/modules/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,484 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Event emitted when a role is granted with an expiry timestamp." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Event emitted when a role is granted with an expiry timestamp. - - - -- Time-bound role assignments with explicit expiry timestamps. -- Functions to check if a role has expired (`isRoleExpired`) or is currently valid (`requireValidRole`). -- Ability to grant roles with future expiry dates, facilitating scheduled access changes. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlTemporalMod provides time-bound role assignments within a Compose diamond. This module enables granting roles that automatically expire, enhancing security and access management by ensuring privileges are not permanent unless explicitly renewed. It integrates seamlessly with the diamond's storage pattern for robust, upgradeable access control. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. **Returns** - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. **Parameters** **Returns** - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. **Returns** - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. **Parameters** **Returns** - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. **Parameters** **Returns** - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - error: AccessControlUnauthorizedAccount If the account does not have the role. - error: AccessControlRoleExpired If the role has expired. **Parameters** - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. **Parameters** **Returns** - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. **Parameters** -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. **Parameters** -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. **Parameters** -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. **Parameters** -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/modules/AccessControlTemporalMod.sol"; - -contract MyFacet { - address constant ACCESS_CONTROL_TEMPORAL_MODULE = address(0x123); // Replace with actual module address - - IAccessControlTemporalMod accessControlTemporalMod = IAccessControlTemporalMod(ACCESS_CONTROL_TEMPORAL_MODULE); - - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - - /** - * @notice Grants an admin role to an account with a specific expiry. - * @param _account The account to grant the role to. - * @param _expiry The timestamp when the role expires. - */ - function grantAdminRoleWithExpiry(address _account, uint64 _expiry) external { - accessControlTemporalMod.grantRoleWithExpiry(ADMIN_ROLE, _account, _expiry); - } - - /** - * @notice Checks if a user has a valid, non-expired admin role. - * @param _account The account to check. - */ - function checkAdminRole(address _account) external { - accessControlTemporalMod.requireValidRole(ADMIN_ROLE, _account); - } -}`} - - -## Best Practices - - -- Use `grantRoleWithExpiry` to assign time-limited permissions, ensuring roles do not persist indefinitely. -- Implement checks using `requireValidRole` to enforce current, non-expired role access before critical operations. -- Design role expiration timestamps carefully, considering operational needs and security implications to avoid unintended access revocations or lingering privileges. - - -## Integration Notes - - -AccessControlTemporalMod stores its state within the diamond's storage. Facets interact with this module via its interface. The `grantRoleWithExpiry`, `revokeTemporalRole`, `getRoleExpiry`, and `isRoleExpired` functions operate on the module's dedicated storage slots, which are managed by the diamond proxy. Changes to role assignments and their expiry are immediately visible to all facets through the module's interface. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx deleted file mode 100644 index e60e8131..00000000 --- a/website/docs/contracts/modules/DiamondCutMod.mdx +++ /dev/null @@ -1,385 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Documentation for DiamondCutMod" -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Documentation for DiamondCutMod - - - -- Allows dynamic addition, replacement, and removal of facets and their functions. -- Supports executing an arbitrary function via `delegatecall` immediately after a diamond cut operation, enabling complex initialization or state changes. -- Provides a `getStorage` function for inspecting facet storage, aiding in debugging and understanding diamond state. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCutMod provides the core functionality for managing facets within a Compose diamond. It enables dynamic addition, replacement, and removal of functions, ensuring the diamond's capabilities can be upgraded and extended safely. This module is crucial for maintaining the diamond's composability and adaptability. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -**Note:** storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall **Parameters** - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-proxy/contracts/interfaces/IDiamondCut.sol"; - -contract MyFacet { - IDiamondCut public diamondCut = IDiamondCut(address(this)); // Assuming diamondCut is accessible - - /** - * @notice Adds new functions to the diamond. - * @param _facetAddress The address of the facet contract. - * @param _functionSelectors An array of function selectors to add. - */ - function addMyFunctions(address _facetAddress, bytes4[] memory _functionSelectors) external { - // Example of adding functions to the diamond - diamondCut.diamondCut( - DiamondCutMod.FacetCut[] - (new DiamondCutMod.FacetCut[](1)) - , - address(0), // Not replacing - bytes('') // Not executing - ); - - // Note: In a real scenario, you would construct the FacetCut struct properly. - // The above is a simplified representation for demonstration. - } - - // Other functions in MyFacet... -}`} - - -## Best Practices - - -- Ensure all calls to `diamondCut` are properly authorized to prevent unauthorized modifications to the diamond's function mappings. -- Carefully validate the `facetAddress` and `functionSelectors` passed to `addFunctions`, `removeFunctions`, and `replaceFunctions` to avoid introducing malicious or unintended logic. -- Use custom errors for revert reasons to provide clear, gas-efficient feedback on cut operation failures. - - -## Integration Notes - - -The DiamondCutMod directly manipulates the diamond's internal storage that maps function selectors to facet addresses and their implementations. Any changes made via `diamondCut`, `addFunctions`, `removeFunctions`, or `replaceFunctions` are immediately reflected in the diamond proxy's routing logic. Facets should be aware that the implementation address for a given function selector can change, and they should always call functions through the diamond proxy's address to ensure they are routed to the correct, current implementation. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx deleted file mode 100644 index 25e6c634..00000000 --- a/website/docs/contracts/modules/DiamondMod.mdx +++ /dev/null @@ -1,236 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Diamond Library - Internal functions and storage for diamond proxy functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond Library - Internal functions and storage for diamond proxy functionality. - - - -- Enables dynamic facet registration during initial deployment via `addFacets`. -- Provides a `diamondFallback` mechanism to route external calls to the appropriate facet. -- Exposes `getStorage` for retrieving the diamond's internal storage layout. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondMod library provides essential internal functions for managing diamond proxy facets and handling function calls. It is crucial for diamond deployment and runtime operation, enabling dynamic facet addition and robust fallback mechanisms for function execution. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -**Note:** storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondMod} from "@compose/diamond-proxy/contracts/DiamondMod.sol"; - -contract MyFacet { - DiamondMod internal diamondMod; - - constructor(address diamondModAddress) { - diamondMod = DiamondMod(diamondModAddress); - } - - /** - * @notice Example of calling getStorage via DiamondMod. - */ - function readDiamondStorage() external view returns (bytes memory) { - return diamondMod.getStorage(); - } -}`} - - -## Best Practices - - -- Use `addFacets` exclusively during diamond deployment to ensure deterministic facet registration. -- Implement custom errors or revert strings within facets to clearly indicate failures during `diamondFallback` execution. -- Ensure that `DiamondMod` is initialized correctly within the diamond proxy's deployment or upgrade process. - - -## Integration Notes - - -DiamondMod interacts directly with the diamond proxy's storage. The `addFacets` function modifies the mapping of function selectors to facet addresses, which is fundamental to the diamond's operation. `diamondFallback` relies on this mapping to dispatch calls. `getStorage` returns the raw storage data, which facets can interpret based on their own storage layouts. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx deleted file mode 100644 index cd20d4d3..00000000 --- a/website/docs/contracts/modules/ERC1155Mod.mdx +++ /dev/null @@ -1,624 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "ERC-1155 Token Receiver Interface - Handles the receipt of a single ERC-1155 token type." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-1155 Token Receiver Interface - Handles the receipt of a single ERC-1155 token type. - - - -- Implements standard ERC-1155 `safeTransferFrom` and `safeBatchTransferFrom` for secure token transfers. -- Supports minting and burning of single tokens and batches, including receiver validation for contract addresses. -- Provides functionality to set and manage base and token-specific URIs for metadata representation. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC1155Mod facet provides a robust implementation for handling ERC-1155 token operations within a Compose diamond. It manages token minting, burning, transfers, and URI management, ensuring adherence to EIP-1155 standards. This module is crucial for any diamond requiring fungible and non-fungible token capabilities, offering a composable and upgradeable solution. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. **Note:** storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. **Parameters** - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. **Parameters** - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. **Returns** - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. **Parameters** - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. **Parameters** - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. **Parameters** - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. **Parameters** - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. **Parameters** - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. **Parameters** - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. **Parameters** -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. **Parameters** -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. **Parameters** -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. **Parameters** -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. **Parameters** -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. **Parameters** -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. **Parameters** -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. **Parameters** -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import {IDiamondCut} from "../diamond/interfaces/IDiamondCut.sol"; - -// Assume ERC1155ModFacet is deployed and its address is known -contract MyDiamondConsumer { - address internal diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - // Example function to mint ERC1155 tokens - function mintErc1155Tokens(address _to, uint256 _id, uint256 _amount, bytes memory _data) external { - // Call the ERC1155Mod facet via the diamond proxy - (bool success, ) = diamondAddress.call(abi.encodeWithSelector( - ERC1155Mod.mint.selector, - _to, - _id, - _amount, - _data - )); - require(success, "ERC1155Mod: mint call failed"); - } - - // Example function to transfer ERC1155 tokens - function transferErc1155Tokens(address _from, address _to, uint256 _id, uint256 _amount, bytes memory _data) external { - // Call the ERC1155Mod facet via the diamond proxy - (bool success, ) = diamondAddress.call(abi.encodeWithSelector( - ERC1155Mod.safeTransferFrom.selector, - _from, - _to, - _id, - _amount, - _data - )); - require(success, "ERC1155Mod: safeTransferFrom call failed"); - } -}`} - - -## Best Practices - - -- Ensure proper access control is implemented at the diamond level for sensitive functions like minting and burning. -- Always validate receiver addresses, especially for contract recipients, to prevent unintended token loss or unexpected behavior. -- Be mindful of gas costs when performing batch operations (mintBatch, burnBatch, safeBatchTransferFrom) with large arrays. - - -## Integration Notes - - -The ERC1155Mod facet interacts with diamond storage to manage token balances, approvals, and URI configurations. Token balances are stored in mappings within the diamond's storage. The `getStorage` function provides direct access to the ERC1155 storage struct via inline assembly, allowing other facets to read the state. Any modifications to token balances or URIs by this facet are immediately reflected in the diamond's global state. Facets extending ERC1155 functionality should be aware of the storage layout and potential for collisions if adding new state. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx deleted file mode 100644 index 4268ea9e..00000000 --- a/website/docs/contracts/modules/ERC165Mod.mdx +++ /dev/null @@ -1,159 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. - - - -- Standard ERC-165 interface detection capabilities. -- Manages interface registration and querying via internal functions. -- Enables facets to declare their supported interfaces programmatically. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -ERC165Mod provides essential functionality for ERC-165 interface detection within a Compose diamond. It manages the registration and querying of supported interfaces, ensuring compliance with the ERC-165 standard. This module is critical for enabling diamond facets to correctly advertise their capabilities to external callers. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. **Returns** - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` **Parameters** - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC165Mod} from "@compose/modules/ERC165Mod.sol"; -import {ERC165Mod} from "@compose/modules/ERC165Mod.sol"; - -contract MyFacet { - // Assuming ERC165Mod is already initialized and its storage pointer is known. - // In a real scenario, you would get this from the Diamond contract. - address constant ERC165_MODULE_ADDRESS = address(0x1); // Placeholder - - function initialize() external { - // Example: Registering ERC721 interface during facet initialization - ERC165Mod.registerInterface(ERC165_MODULE_ADDRESS, type(IERC721).interfaceId); - } - - function supportsInterface(bytes4 interfaceId) external view returns (bool) { - // Example: Checking if an interface is supported - return ERC165Mod.supportsInterface(ERC165Mod.getStorage(ERC165_MODULE_ADDRESS), interfaceId); - } -}`} - - -## Best Practices - - -- Register all supported interfaces during facet initialization to ensure accurate reporting. -- Use the `supportsInterface` function provided by the module to check for interface support, rather than directly accessing storage. -- Ensure the ERC165Mod is correctly initialized and accessible within the diamond's upgrade process. - - -## Integration Notes - - -The ERC165Mod utilizes a specific storage slot for its `ERC165Storage` struct, as managed by the diamond proxy. Facets interact with this module by calling its external functions, which in turn use inline assembly to access and manipulate the `ERC165Storage` at its designated position. The `getStorage` function is crucial for obtaining a pointer to this storage, allowing other facets to interact with the ERC-165 state. It's imperative that the `ERC165Storage` struct definition and its slot remain consistent across upgrades. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx deleted file mode 100644 index ba2884b1..00000000 --- a/website/docs/contracts/modules/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,439 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "LibERC20Bridgeable — ERC-7802 Library - Revert when a provided receiver is invalid(e.g,zero address) ." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Bridgeable — ERC-7802 Library - Revert when a provided receiver is invalid(e.g,zero address) . - - - -- Enforces `trusted-bridge` role for cross-chain burn and mint operations. -- Validates receiver addresses to prevent sending to zero address. -- Provides internal helper functions for access control checks. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20BridgeableMod provides essential functionality for managing cross-chain ERC20 token transfers within a Compose diamond. It enforces access control for trusted bridge operators and ensures the validity of receiver addresses, enhancing the security and reliability of inter-chain token operations. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. **Note:** storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. **Parameters** - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. **Parameters** - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. **Parameters** - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. **Returns** - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. **Parameters** -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. **Parameters** -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. **Parameters** -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. **Parameters** -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. **Parameters** -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. **Parameters** -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . **Parameters** -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC20Bridgeable, IERC20BridgeableMod} from "@compose/modules/ERC20BridgeableMod.sol"; -import {DiamondStorage} from "@compose/core/DiamondStorage.sol"; - -contract ERC20BridgeableFacet { - using LibERC20Bridgeable for DiamondStorage; - - /** - * @notice Burns ERC20 tokens for a cross-chain operation. - * @param _token The address of the ERC20 token. - * @param _to The recipient address on the destination chain. - * @param _amount The amount of tokens to burn. - */ - function burnForCrosschain(address _token, address _to, uint256 _amount) external { - // Ensure the caller is a trusted bridge operator - LibERC20Bridgeable.checkTokenBridge(msg.sender); - - // Perform the cross-chain burn operation - LibERC20Bridgeable.crosschainBurn(_token, _to, _amount); - } - - /** - * @notice Mints ERC20 tokens for a cross-chain operation. - * @param _token The address of the ERC20 token. - * @param _to The recipient address on the destination chain. - * @param _amount The amount of tokens to mint. - */ - function mintForCrosschain(address _token, address _to, uint256 _amount) external { - // Ensure the caller is a trusted bridge operator - LibERC20Bridgeable.checkTokenBridge(msg.sender); - - // Perform the cross-chain mint operation - LibERC20Bridgeable.crosschainMint(_token, _to, _amount); - } -}`} - - -## Best Practices - - -- Only addresses with the `trusted-bridge` role should be allowed to call `crosschainBurn` and `crosschainMint` functions. -- Always validate the receiver address (`_to`) to prevent accidental token loss or sending to zero address. -- Ensure the `ERC20BridgeableMod` is correctly initialized with trusted bridge addresses. - - -## Integration Notes - - -The `ERC20BridgeableMod` relies on the `AccessControl` module for managing the `trusted-bridge` role. It also interacts with ERC20 token contracts directly. The `getAccessControlStorage` and `getERC20Storage` functions provide internal access to these respective storage layouts, ensuring that facets can correctly interact with the underlying storage slots managed by the diamond. Changes to the `trusted-bridge` role within `AccessControl` will directly impact the authorization of bridge operations. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx deleted file mode 100644 index f2b7faf7..00000000 --- a/website/docs/contracts/modules/ERC20Mod.mdx +++ /dev/null @@ -1,425 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "LibERC20 — ERC-20 Library - Thrown when a sender attempts to transfer or burn more tokens than their balance." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20 — ERC-20 Library - Thrown when a sender attempts to transfer or burn more tokens than their balance. - - - -- Implements standard ERC-20 functions: mint, burn, transfer, transferFrom, approve. -- Manages total supply and individual token balances. -- Supports allowance mechanism for delegated token transfers. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod module provides a standard implementation for ERC-20 token functionality within a Compose diamond. It enables core operations like minting, burning, transferring, and approving token allowances, ensuring consistent and auditable token management across diamond applications. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. **Parameters** - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. **Parameters** - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. **Returns** - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. **Parameters** - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. **Parameters** - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. **Parameters** - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. **Parameters** -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. **Parameters** -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. **Parameters** -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. **Parameters** -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC20} from "path/to/LibERC20.sol"; -import {IERC20Mod} from "path/to/IERC20Mod.sol"; - -contract MyERC20Facet { - address immutable _diamondAddress; - - constructor(address diamondAddress) { - _diamondAddress = diamondAddress; - } - - function mintTokens(address _to, uint256 _amount) external { - IERC20Mod(_diamondAddress).mint(_to, _amount); - } - - function transferTokens(address _to, uint256 _amount) external { - IERC20Mod(_diamondAddress).transfer(msg.sender, _to, _amount); - } - - function approveSpending(address _spender, uint256 _amount) external { - IERC20Mod(_diamondAddress).approve(msg.sender, _spender, _amount); - } -}`} - - -## Best Practices - - -- Always use custom errors for revert conditions, such as insufficient balance or allowance, for gas efficiency and clarity. -- Ensure that all external callers interact with the diamond proxy address, not directly with facet implementations. -- When upgrading facets, be aware of storage layout changes and ensure compatibility to prevent data loss or corruption. - - -## Integration Notes - - -The ERC20Mod relies on a specific storage slot for its ERC-20 state. The `getStorage` function provides access to this state via inline assembly, ensuring it's correctly bound to the diamond's storage. Facets interacting with ERC20Mod must use the diamond proxy address and call the functions exposed by the `IERC20Mod` interface. Any changes to the ERC-20 storage layout within the module must be carefully managed to maintain compatibility with existing facets and diamond upgrades. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx deleted file mode 100644 index d63ddc8d..00000000 --- a/website/docs/contracts/modules/ERC20PermitMod.mdx +++ /dev/null @@ -1,279 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Thrown when a permit signature is invalid or expired." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Permit — Library for ERC-2612 Permit Logic - Thrown when a permit signature is invalid or expired. - - - -- Implements the ERC-2612 standard for off-chain signature-based token approvals. -- Utilizes a dedicated storage slot for permit-related data, ensuring composability and upgrade safety. -- Provides access to the `DOMAIN_SEPARATOR` for consistent signature generation across facets. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20PermitMod provides the necessary logic for implementing the ERC-2612 'permit' functionality within a Compose diamond. This allows users to grant allowances to other addresses via signed off-chain messages, enhancing user experience and reducing gas costs for frequent allowance updates. - ---- - -## Storage - -### ERC20PermitStorage - -**Note:** storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -**Note:** storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for &#123;permit&#125;. This value is unique to a contract and chain ID combination to prevent replay attacks. **Returns** - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. **Parameters** - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. **Parameters** -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. **Parameters** -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC20Permit} from "@compose-protocol/diamond-contracts/contracts/module/erc20/LibERC20Permit.sol"; -import {IERC20Permit} from "@compose-protocol/diamond-contracts/contracts/interface/IERC20Permit.sol"; - -contract MyERC20Facet { - using LibERC20Permit for LibERC20Permit.PermitStorage; - - function permit(IERC20Permit._Permit calldata _permit, bytes32 _signature) external { - LibERC20Permit.PermitStorage storage ps = LibERC20Permit.getPermitStorage(); - address owner = ps.permit(_permit, _signature); - // The permit function emits the Approval event. - // If this facet needs to emit an Approval event, it must be done here. - // For example: emit Approval(owner, _permit.spender, _permit.value); - } - - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return LibERC20Permit.DOMAIN_SEPARATOR(); - } -}`} - - -## Best Practices - - -- Ensure the `permit` function is called within a facet that correctly emits the `Approval` event as specified by ERC-20 standards, as `LibERC20Permit.permit` itself does not emit it. -- Always verify the `DOMAIN_SEPARATOR` is correctly configured and matches the chain ID and contract address when implementing off-chain permit signing. -- Treat signature validation as a critical security step; ensure all parameters for `LibERC20Permit.permit` are accurately passed from the signed message. - - -## Integration Notes - - -The ERC20PermitMod interacts with the diamond's storage through the `LibERC20Permit.PermitStorage` struct. Facets integrating this module must retrieve this storage using `LibERC20Permit.getPermitStorage()`. The `permit` function within the library validates signatures and updates the allowance, but the responsibility of emitting the `Approval` event lies with the calling facet, ensuring adherence to the ERC-20 standard's event emission requirements. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx deleted file mode 100644 index cf8a7151..00000000 --- a/website/docs/contracts/modules/ERC6909Mod.mdx +++ /dev/null @@ -1,535 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "LibERC6909 — ERC-6909 Library - Thrown when the sender has insufficient balance." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC6909 — ERC-6909 Library - Thrown when the sender has insufficient balance. - - - -- Implements the core ERC-6909 token logic including minting, burning, transfers, and approvals. -- Supports both fungible and non-fungible token types via token IDs. -- Includes operator functionality, allowing accounts to manage tokens on behalf of others. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC6909Mod provides a standardized implementation for the ERC-6909 token standard, enabling fungible and non-fungible tokens within a Compose diamond. It manages token balances, approvals, and operator relationships, crucial for composable asset management in decentralized applications. - ---- - -## Storage - -### ERC6909Storage - -**Note:** storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. **Parameters** - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. **Parameters** - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. **Returns** - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. **Parameters** - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. **Parameters** - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. **Parameters** - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. **Parameters** -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. **Parameters** -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. **Parameters** -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. **Parameters** -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. **Parameters** -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. **Parameters** -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. **Parameters** -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. **Parameters** -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. **Parameters** -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909} from "@compose/contracts/src/interfaces/IERC6909.sol"; -import {ERC6909Mod} from "@compose/contracts/src/modules/ERC6909/ERC6909Mod.sol"; - -contract MyTokenFacet { - using ERC6909Mod for address; - - address immutable DIAMOND_ADDRESS; - - constructor(address _diamondAddress) { - DIAMOND_ADDRESS = _diamondAddress; - } - - /** - * @notice Mints tokens to a recipient. - * @param _to The address to mint tokens to. - * @param _id The ID of the token to mint. - * @param _amount The amount of tokens to mint. - */ - function mintTokens(address _to, uint256 _id, uint256 _amount) external { - DIAMOND_ADDRESS.mint(_id, _to, _amount); - } - - /** - * @notice Transfers tokens from one address to another. - * @param _from The address to transfer tokens from. - * @param _to The address to transfer tokens to. - * @param _id The ID of the token to transfer. - * @param _amount The amount of tokens to transfer. - */ - function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { - DIAMOND_ADDRESS.transfer(_id, _from, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure access control is correctly implemented in facets calling ERC6909Mod functions, especially for sensitive operations like minting and burning. -- Handle potential `ERC6909Mod.InsufficientBalance` errors gracefully in your facets. -- Understand that ERC6909Mod functions operate on the diamond's storage; changes are persistent and upgrade-safe as long as the storage layout is maintained. - - -## Integration Notes - - -The ERC6909Mod interacts directly with the diamond's storage. It defines its own storage slot via `STORAGE_POSITION` to hold the `ERC6909Storage` struct. Facets that use ERC6909Mod must be aware of this storage layout and ensure no conflicts arise with other modules or facets. The `getStorage` function provides a pointer to this struct for direct inspection if needed, but direct manipulation is discouraged in favor of calling the provided functions. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx deleted file mode 100644 index 11ae3b5e..00000000 --- a/website/docs/contracts/modules/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,353 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "ERC-721 Enumerable Library for Compose - Thrown when attempting to interact with a non-existent token." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Enumerable Library for Compose - Thrown when attempting to interact with a non-existent token. - - - -- Automatically updates token enumeration lists during mint, burn, and transfer operations. -- Provides an explicit `getStorage` function to retrieve the ERC-721 enumerable storage struct, useful for debugging or advanced querying. -- Enforces basic validation for token existence, receiver address, and ownership during operations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721EnumerableMod provides essential functionality for managing ERC-721 tokens within a Compose diamond, specifically addressing the enumeration of tokens. It ensures that tokens are correctly added and removed from internal tracking lists during minting, burning, and transfers, maintaining a consistent and auditable token supply. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. **Parameters** - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. **Returns** - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. **Parameters** - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. **Parameters** - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. **Parameters** -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. **Parameters** -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. **Parameters** -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. **Parameters** -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. **Parameters** -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. **Parameters** -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod} from "@compose/modules/ERC721/ERC721EnumerableMod.sol"; -import {IERC721Storage} from "@compose/storage/ERC721Storage.sol"; - -contract MyERC721Facet { - // Assume IERC721EnumerableMod is registered at a specific selector in the diamond. - // Assume IERC721Storage is accessible via diamond storage. - - function mintToken(address _to, uint256 _tokenId) external { - // Access the ERC721EnumerableMod interface - IERC721EnumerableMod enumerableMod = IERC721EnumerableMod(address(this)); - - // Mint the token, which will also update enumeration lists - enumerableMod.mint(_to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - // Access the ERC721EnumerableMod interface - IERC721EnumerableMod enumerableMod = IERC721EnumerableMod(address(this)); - - // Burn the token, which will also remove it from enumeration lists - enumerableMod.burn(_tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - // Access the ERC721EnumerableMod interface - IERC721EnumerableMod enumerableMod = IERC721EnumerableMod(address(this)); - - // Transfer the token, which updates enumeration data - enumerableMod.transferFrom(_from, _to, _tokenId); - } -}`} - - -## Best Practices - - -- Ensure proper access control is implemented in calling facets for `mint`, `burn`, and `transferFrom` operations, as the module itself does not enforce caller authorization beyond basic checks. -- Always verify that the `_tokenId` exists before attempting to burn it, and that the receiver address is not zero when minting, to prevent unexpected reverts. -- Be mindful of storage layout changes. This module relies on a specific structure for ERC721 enumerable data; any modifications to the underlying storage slot could break its functionality. - - -## Integration Notes - - -The ERC721EnumerableMod interacts with the ERC-721 storage pattern, specifically managing data within a predefined storage slot dedicated to enumerable ERC-721 tokens. The `getStorage` function uses inline assembly to directly access this slot. Any facet that utilizes this module must ensure that the diamond's storage layout includes the correct storage slot for ERC721 enumerable data and that this module is correctly registered within the diamond proxy. The module's operations are designed to be atomic with respect to the token's lifecycle, ensuring that enumeration data is always consistent with the token's existence and ownership. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx deleted file mode 100644 index 4d241df7..00000000 --- a/website/docs/contracts/modules/ERC721Mod.mdx +++ /dev/null @@ -1,365 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "ERC-721 Library for Compose - Thrown when attempting to interact with a non-existent token." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Library for Compose - Thrown when attempting to interact with a non-existent token. - - - -- Supports core ERC-721 operations: minting, burning, and transferring tokens. -- Enforces token existence checks for burn and transfer operations. -- Manages token ownership and approvals as per ERC-721 standard. -- Provides `getStorage` to inspect the module's internal ERC-721 state. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod provides essential ERC-721 token functionalities for Compose diamonds. It enables minting, burning, and transferring of NFTs, ensuring proper ownership and approval management. This module is crucial for implementing non-fungible token standards within a composable diamond architecture, allowing for safe and upgradeable NFT logic. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. **Parameters** - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. **Returns** - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. **Parameters** - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. **Parameters** - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. **Parameters** -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. **Parameters** -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. **Parameters** -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). **Parameters** -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. **Parameters** -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; - -contract MyNFTPartialFacet { - // Assume IERC721Mod is initialized and accessible via the diamond proxy - IERC721Mod public immutable erc721Mod; - - constructor(address _diamondProxy) { - erc721Mod = IERC721Mod(_diamondProxy); - } - - function mintNewToken(address _to, uint256 _tokenId) external { - // Example of calling mint from the module - erc721Mod.mint(_to, _tokenId); - } - - function transferExistingToken(address _from, address _to, uint256 _tokenId) external { - // Example of calling transferFrom from the module - erc721Mod.transferFrom(_from, _to, _tokenId); - } - - function destroyToken(uint256 _tokenId) external { - // Example of calling burn from the module - erc721Mod.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721Mod` contract is correctly initialized and accessible via the diamond proxy before calling its functions. -- Handle potential reverts from functions like `mint` (token exists, zero address) and `burn`/`transferFrom` (token does not exist) using `try/catch` or by ensuring preconditions are met. -- Be mindful of storage slot collisions if implementing custom ERC-721 logic; refer to `getStorage` for the module's storage layout. - - -## Integration Notes - - -The ERC721Mod utilizes a predefined storage slot for its ERC-721 data structure. Facets interacting with this module should be aware of this storage layout, especially when using the `getStorage` function. Modifications made through `mint`, `burn`, or `transferFrom` directly update this shared storage, making them immediately visible to all facets that access the same storage slot. No specific invariants or ordering requirements beyond standard ERC-721 logic are imposed by this module itself. - - -
- -
- - diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx deleted file mode 100644 index 01ffde9d..00000000 --- a/website/docs/contracts/modules/NonReentrancyMod.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. - - - -- Prevents reentrancy by maintaining an internal non-reentrancy lock state. -- Provides explicit `enter` and `exit` functions for clear control flow. -- Designed for integration within Compose diamond facets. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod module provides essential utilities for preventing reentrancy attacks within your diamond. By utilizing its `enter` and `exit` functions, facets can enforce strict execution order, ensuring that critical operations are not interrupted by recursive calls, thereby enhancing the security and integrity of your diamond's state. - ---- - -## Storage - ---- -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose/modules/NonReentrancyMod/LibNonReentrancy.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 public _nonReentrancyState; // Example state protected by non-reentrancy - - /** - * @notice Performs a protected operation. - */ - function protectedOperation() external { - // Enter the non-reentrancy guard - _nonReentrancyState = _nonReentrancyState.enter(); - - // Perform critical state changes here... - // For example, emit an event, update storage, etc. - - // Exit the non-reentrancy guard - _nonReentrancyState = _nonReentrancyState.exit(); - } -}`} - - -## Best Practices - - -- Always pair `enter()` with a corresponding `exit()` call to ensure the guard is properly released, even in error scenarios (e.g., using `try/catch` or `require` statements that might revert before `exit()`). -- Use `LibNonReentrancy.enter()` at the very beginning of a function and `LibNonReentrancy.exit()` at the very end to provide the broadest protection. -- Ensure the variable used with `LibNonReentrancy` is appropriately scoped and managed within the facet's storage. - - -## Integration Notes - - -The NonReentrancyMod is intended to be used as a library within facets. Facets will typically declare a storage variable (e.g., `uint256 _nonReentrancyState`) and use the `LibNonReentrancy` functions with this variable to manage the reentrancy lock. The state managed by this library is local to the facet's execution context and does not directly interact with or modify the diamond's shared storage slots. Each facet using this module is responsible for its own reentrancy protection. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx deleted file mode 100644 index 92ad053b..00000000 --- a/website/docs/contracts/modules/OwnerMod.mdx +++ /dev/null @@ -1,251 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. - - - -- Manages contract ownership according to ERC-173 standards. -- Provides a `requireOwner` modifier for access control. -- Supports ownership renouncement by transferring to `address(0)`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod provides essential ERC-173 contract ownership functionality. It manages the contract owner's address, allowing for secure ownership transfers and owner-based access control, crucial for administrative operations within a diamond. - ---- - -## Storage - -### OwnerStorage - -**Note:** storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. **Returns** - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner **Returns** - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. **Parameters** - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; -import {OwnerStorage} from "@compose/modules/owner/OwnerStorage.sol"; - -contract MyOwnerFacet { - address constant STORAGE_POSITION = 0x0; // Example storage slot - - function getOwner() external view returns (address) { - // Access the OwnerMod storage layout - OwnerStorage storage ownerStorage = OwnerStorage(STORAGE_POSITION); - // Call the owner function from the module interface - return IOwnerMod(address(ownerStorage)).owner(); - } - - function transferContractOwnership(address _newOwner) external { - OwnerStorage storage ownerStorage = OwnerStorage(STORAGE_POSITION); - // Call the transfer ownership function from the module interface - IOwnerMod(address(ownerStorage)).transferOwnership(_newOwner); - } -}`} - - -## Best Practices - - -- Use `requireOwner()` to protect sensitive administrative functions within your facet. -- Carefully manage ownership transfers, ensuring the new owner is a trusted address. -- Implement checks for `address(0)` when transferring ownership to allow for renouncement. - - -## Integration Notes - - -The OwnerMod utilizes a specific storage slot for its `OwnerStorage` struct. Facets interacting with ownership functions must access this storage slot directly or via the `IOwnerMod` interface. The `getStorage` function can be used to retrieve a pointer to this storage if the `STORAGE_POSITION` is known. Changes to the owner address are immediately reflected across all facets interacting with the `OwnerStorage`. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx deleted file mode 100644 index 5f1ec77a..00000000 --- a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,322 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. - - - -- Two-step ownership transfer process for enhanced security. -- `requireOwner` modifier for access control to critical functions. -- Provides standard `owner()` and `pendingOwner()` view functions. -- Supports `renounceOwnership()` to set owner to `address(0)`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerTwoStepsMod provides secure, two-step ownership management for Compose diamonds. This pattern prevents accidental ownership loss by requiring explicit acceptance of a new owner, enhancing contract safety and upgradeability. - ---- - -## Storage - -### OwnerStorage - -**Note:** storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -**Note:** storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -Storage position: `OWNER_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. **Returns** - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. **Returns** - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. **Parameters** - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {OwnerTwoStepsMod} from "@compose/modules/owner/OwnerTwoStepsMod.sol"; -import {IOwnerTwoStepsMod} from "@compose/modules/owner/IOwnerTwoStepsMod.sol"; - -contract MyFacet { - OwnerTwoStepsMod private ownerMod; - - // Assume OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are defined and initialized - uint256 constant OWNER_STORAGE_POSITION = 1; // Example slot - uint256 constant PENDING_OWNER_STORAGE_POSITION = 2; // Example slot - - constructor(address diamondProxy) { - ownerMod = new OwnerTwoStepsMod(diamondProxy, OWNER_STORAGE_POSITION, PENDING_OWNER_STORAGE_POSITION); - } - - /** - * @notice Transfers ownership to a new address. - * @param _newOwner The address to transfer ownership to. - */ - function initiateOwnershipTransfer(address _newOwner) external { - ownerMod.transferOwnership(_newOwner); - } - - /** - * @notice Accepts pending ownership transfer. - */ - function acceptNewOwnership() external { - ownerMod.acceptOwnership(); - } - - /** - * @notice Gets the current owner. - * @return The current owner address. - */ - function getCurrentOwner() external view returns (address) { - return ownerMod.owner(); - } - - /** - * @notice Renounces ownership of the contract. - */ - function removeOwnership() external { - ownerMod.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Always use the `transferOwnership` function to initiate a transfer, followed by the new owner calling `acceptOwnership`. -- Protect sensitive functions by calling `requireOwner()` to ensure only the current owner can execute them. -- Ensure `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are unique and correctly set during diamond initialization to prevent storage collisions. - - -## Integration Notes - - -This module utilizes inline assembly to read and write to specific storage slots defined by `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION`. These slots must be unique and managed by the diamond proxy's storage layout. Facets integrating this module should ensure these positions do not conflict with other facets' storage. The module directly manipulates the owner and pending owner state, making these accessible to any facet that queries the module or the diamond's storage directly. - - -
- -
- - diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx deleted file mode 100644 index fc7268c6..00000000 --- a/website/docs/contracts/modules/RoyaltyMod.mdx +++ /dev/null @@ -1,367 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "LibRoyalty - ERC-2981 Royalty Standard Library - Thrown when default royalty fee exceeds 100% (10000 basis points)." -gitSource: "https://github.com/maxnorm/Compose/blob/7cec432f52f2cad53f4d546df90deb49e6a66d41/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibRoyalty - ERC-2981 Royalty Standard Library - Thrown when default royalty fee exceeds 100% (10000 basis points). - - - -- Implements the ERC-2981 `royaltyInfo` standard, providing a standardized way to query royalty details. -- Supports both a global default royalty setting and token-specific overrides, offering flexible royalty management. -- Includes functions to explicitly delete or reset royalty information, allowing for dynamic adjustments to royalty configurations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The RoyaltyMod provides an implementation of the ERC-2981 royalty standard for Compose diamonds. It allows setting default royalties and token-specific overrides, ensuring that creators are compensated on secondary sales. This module is crucial for marketplaces and NFT platforms built on Compose, enabling composable royalty logic. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -**Note:** storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. **Returns** - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. **Parameters** - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. **Parameters** **Returns** - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. **Parameters** - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. **Parameters** - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). **Parameters** -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. **Parameters** -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). **Parameters** -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. **Parameters** -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "@compose/contracts/modules/royalty/IRoyaltyMod.sol"; - -contract MyRoyaltyFacet { - // Assume IRoyaltyMod is correctly implemented and accessible via the diamond proxy - IRoyaltyMod internal royaltyMod; - - // Initialize with the diamond proxy address - function initialize(address _diamondProxy) public { - royaltyMod = IRoyaltyMod(_diamondProxy); - } - - /** - * @notice Sets a default royalty for all tokens. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). - */ - function grantDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setDefaultRoyalty(_receiver, _feeBasisPoints); - } - - /** - * @notice Sets a specific royalty for a token ID. - * @param _tokenId The ID of the token to set royalty for. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). - */ - function grantTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); - } - - /** - * @notice Retrieves royalty information for a token and sale price. - * @param _tokenId The ID of the token. - * @param _salePrice The sale price of the token. - * @return receiver The address entitled to the royalty. - * @return royaltyAmount The calculated royalty amount. - */ - function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { - (receiver, royaltyAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); - return (receiver, royaltyAmount); - } -} -`} - - -## Best Practices - - -- Ensure the `_feeBasisPoints` parameter in `setDefaultRoyalty` and `setTokenRoyalty` does not exceed 10000 (100%) to prevent excessive fees. The library enforces this, but explicit checks in calling facets can add an extra layer of safety. -- When resetting token-specific royalties using `resetTokenRoyalty`, be aware that it reverts to the default royalty settings. Ensure the default royalty is configured appropriately if this action is intended. -- Use custom errors or specific revert messages when calling functions to clearly indicate the reason for failure, especially during royalty setting operations. - - -## Integration Notes - - -The RoyaltyMod interacts with diamond storage to manage default royalty information and token-specific royalty configurations. Facets can access this module via its interface, and calls to functions like `royaltyInfo` will transparently query the appropriate storage slots within the diamond's storage layout. The `getStorage` function provides direct access to the internal storage struct, which is useful for advanced inspection or complex logic but should be used with caution to maintain storage hygiene. Changes to default royalties are immediately reflected in subsequent `royaltyInfo` calls for tokens without specific overrides. Token-specific royalties directly override the default for the specified token ID. - - -
- -
- - From 93dd936ba24c6808536169af9bb3361e39dfdbfd Mon Sep 17 00:00:00 2001 From: maxnorm Date: Fri, 19 Dec 2025 22:28:13 +0000 Subject: [PATCH 31/68] docs: auto-generate docs pages from NatSpec --- .../contracts/facets/AccessControlFacet.mdx | 561 +++++++++++++ .../facets/AccessControlPausableFacet.mdx | 388 +++++++++ .../facets/AccessControlTemporalFacet.mdx | 453 +++++++++++ .../docs/contracts/facets/DiamondCutFacet.mdx | 419 ++++++++++ .../contracts/facets/DiamondLoupeFacet.mdx | 255 ++++++ .../docs/contracts/facets/ERC1155Facet.mdx | 679 ++++++++++++++++ .../contracts/facets/ERC20BridgeableFacet.mdx | 420 ++++++++++ .../docs/contracts/facets/ERC20BurnFacet.mdx | 260 ++++++ website/docs/contracts/facets/ERC20Facet.mdx | 576 +++++++++++++ .../contracts/facets/ERC20PermitFacet.mdx | 350 ++++++++ .../docs/contracts/facets/ERC6909Facet.mdx | 530 ++++++++++++ .../docs/contracts/facets/ERC721BurnFacet.mdx | 205 +++++ .../facets/ERC721EnumerableBurnFacet.mdx | 227 ++++++ .../facets/ERC721EnumerableFacet.mdx | 764 ++++++++++++++++++ website/docs/contracts/facets/ERC721Facet.mdx | 668 +++++++++++++++ .../docs/contracts/facets/ExampleDiamond.mdx | 138 ++++ website/docs/contracts/facets/OwnerFacet.mdx | 210 +++++ .../contracts/facets/OwnerTwoStepsFacet.mdx | 291 +++++++ .../docs/contracts/facets/RoyaltyFacet.mdx | 196 +++++ .../contracts/modules/AccessControlMod.mdx | 446 ++++++++++ .../modules/AccessControlPausableMod.mdx | 387 +++++++++ .../modules/AccessControlTemporalMod.mdx | 480 +++++++++++ .../docs/contracts/modules/DiamondCutMod.mdx | 345 ++++++++ website/docs/contracts/modules/DiamondMod.mdx | 241 ++++++ website/docs/contracts/modules/ERC1155Mod.mdx | 618 ++++++++++++++ website/docs/contracts/modules/ERC165Mod.mdx | 158 ++++ .../contracts/modules/ERC20BridgeableMod.mdx | 439 ++++++++++ website/docs/contracts/modules/ERC20Mod.mdx | 429 ++++++++++ .../docs/contracts/modules/ERC20PermitMod.mdx | 309 +++++++ website/docs/contracts/modules/ERC6909Mod.mdx | 524 ++++++++++++ .../contracts/modules/ERC721EnumerableMod.mdx | 372 +++++++++ website/docs/contracts/modules/ERC721Mod.mdx | 365 +++++++++ .../contracts/modules/NonReentrancyMod.mdx | 142 ++++ website/docs/contracts/modules/OwnerMod.mdx | 250 ++++++ .../contracts/modules/OwnerTwoStepsMod.mdx | 304 +++++++ website/docs/contracts/modules/RoyaltyMod.mdx | 365 +++++++++ 36 files changed, 13764 insertions(+) create mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx create mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx create mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx create mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721Facet.mdx create mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx create mode 100644 website/docs/contracts/facets/OwnerFacet.mdx create mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx create mode 100644 website/docs/contracts/modules/AccessControlMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx create mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx create mode 100644 website/docs/contracts/modules/DiamondMod.mdx create mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx create mode 100644 website/docs/contracts/modules/ERC165Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx create mode 100644 website/docs/contracts/modules/ERC20Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx create mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx create mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx create mode 100644 website/docs/contracts/modules/ERC721Mod.mdx create mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx create mode 100644 website/docs/contracts/modules/OwnerMod.mdx create mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx create mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx new file mode 100644 index 00000000..54007121 --- /dev/null +++ b/website/docs/contracts/facets/AccessControlFacet.mdx @@ -0,0 +1,561 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Contract documentation for AccessControlFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for AccessControlFacet + + + +- Role-Based Access Control (RBAC): Define custom roles and assign them to addresses. +- Granular Permissions: Grant and revoke roles individually or in batches. +- Role Hierarchy Management: Define which roles can administer other roles, enabling flexible permission delegation. + + +## Overview + +The AccessControlFacet provides a robust role-based access control (RBAC) system for your diamond. It allows granular permission management, enabling you to define roles, assign them to addresses, and enforce role-specific access to functions within the diamond. This facet is crucial for orchestrating secure operations and managing administrative privileges. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet} from "@compose-protocol/diamond/contracts/facets/DiamondCutFacet.sol"; +import {DiamondLoupeFacet} from "@compose-protocol/diamond/contracts/facets/DiamondLoupeFacet.sol"; +import {AccessControlFacet} from "@compose-protocol/diamond/contracts/facets/AccessControlFacet.sol"; + +// Assume diamond contract and initialization are handled elsewhere + +contract AccessControlConsumer { + AccessControlFacet public acFacet; + + // Define role selectors (e.g., using interfaces or constants) + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + + constructor(address _diamondAddress) { + // Get the AccessControlFacet from the diamond + acFacet = AccessControlFacet(_diamondAddress); + } + + function grantAdminRole(address _user) external { + // Caller must have the role that is the admin of ADMIN_ROLE (usually DEFAULT_ADMIN_ROLE) + acFacet.grantRole(ADMIN_ROLE, _user); + } + + function revokeOperatorRole(address _user) external { + // Caller must have the role that is the admin of OPERATOR_ROLE + acFacet.revokeRole(OPERATOR_ROLE, _user); + } + + function checkUserHasOperatorRole(address _user) external view returns (bool) { + return acFacet.hasRole(OPERATOR_ROLE, _user); + } + + function onlyOperator() external view { + // Reverts if caller does not have OPERATOR_ROLE + acFacet.requireRole(OPERATOR_ROLE); + // ... perform operator-only actions ... + } +}`} + + +## Best Practices + + +- Initialize roles and their admins during diamond deployment. Use `grantRole` or `grantRoleBatch` for initial assignments. +- Carefully manage role admin assignments. The role that administers another role determines who can grant or revoke the subordinate role. +- Use `requireRole` within your facet functions to enforce access control checks directly, ensuring that only authorized accounts can execute specific logic. + + +## Security Considerations + + +Access control checks are fundamental. Ensure that the caller has the appropriate role before executing sensitive operations. The `setRoleAdmin` function requires the caller to be the current admin of the role being modified. `grantRole` and `revokeRole` require the caller to be the admin of the role being granted or revoked. `renounceRole` allows a user to give up a role they hold, but the caller must be the account renouncing the role. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..30d711fe --- /dev/null +++ b/website/docs/contracts/facets/AccessControlPausableFacet.mdx @@ -0,0 +1,388 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Contract documentation for AccessControlPausableFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for AccessControlPausableFacet + + + +- Role-specific pausing: Allows individual roles to be paused independently, providing fine-grained control. +- Admin-controlled operations: Only the designated administrator of a role can pause or unpause it, ensuring secure management. +- Emergency stop mechanism: Enables rapid disabling of role functionalities during critical events or maintenance. + + +## Overview + +The AccessControlPausableFacet provides granular control over role-based access by integrating pausing functionality. It allows specific roles to be temporarily disabled, preventing their associated operations while maintaining the underlying access control structure. This facet is crucial for managing emergency situations or performing maintenance without revoking permissions entirely. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose-protocol/diamond-proxy/facets/DiamondLoupeFacet.sol"; +import {AccessControlPausableFacet} from "@compose-protocol/diamond-proxy/facets/AccessControlPausableFacet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-proxy/interfaces/IDiamondCut.sol"; + +contract DeployDiamond { + function deploy() external { + // ... deployment logic for diamond proxy and initial facets ... + + AccessControlPausableFacet accessControlPausableFacet = new AccessControlPausableFacet(); + + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: address(accessControlPausableFacet), + action: IDiamondCut.FacetCutAction.ADD, + selectors: + DiamondLoupeFacet.getSelectors(accessControlPausableFacet) + }); + + // ... diamondCut call to add the AccessControlPausableFacet ... + } +} + +contract User { + AccessControlPausableFacet accessControlPausableFacet; + + function useAccessControlPausable(address diamondAddress) external { + accessControlPausableFacet = AccessControlPausableFacet(diamondAddress); + + // Example: Check if a role is paused + bool isPaused = accessControlPausableFacet.isRolePaused("someRole"); + + // Example: Pause a role (assuming caller is admin) + // accessControlPausableFacet.pauseRole("someRole"); + + // Example: Unpause a role (assuming caller is admin) + // accessControlPausableFacet.unpauseRole("someRole"); + } +}`} + + +## Best Practices + + +- Initialize roles and their administrators using the underlying AccessControl mechanism before attempting to pause or unpause them. +- Ensure that the caller has the necessary administrative privileges to pause or unpause a role, as enforced by the facet. +- Integrate `requireRoleNotPaused` checks within your application logic to prevent operations associated with paused roles. + + +## Security Considerations + + +This facet relies on the underlying AccessControl system for role management. Ensure that role administration is correctly configured to prevent unauthorized pausing or unpausing. The `requireRoleNotPaused` function is critical for preventing unintended operations when a role is paused. Reentrancy is not a direct concern for the pause/unpause functions themselves, but downstream logic called after a role is unpaused should be audited for reentrancy vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..8433f8cb --- /dev/null +++ b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx @@ -0,0 +1,453 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Contract documentation for AccessControlTemporalFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for AccessControlTemporalFacet + + + +- Grants roles with a specific expiry timestamp. +- Provides a mechanism to check if a role assignment is currently valid and has not expired. +- Supports revoking temporal roles before their expiry. +- Enforces access control based on role validity and expiry. + + +## Overview + +The AccessControlTemporalFacet extends standard role-based access control by introducing time-bound role assignments. It allows granting roles with specific expiry timestamps and checking for their validity, enabling dynamic permission management within a Compose diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose/diamond-contracts/contracts/interfaces/IAccessControl.sol"; +import {IAccessControlTemporal} from "@compose/diamond-contracts/contracts/interfaces/IAccessControlTemporal.sol"; + +// Assume diamond proxy and facet addresses are known +address constant DIAMOND_ADDRESS = address(0x123...); +address constant ACCESS_CONTROL_TEMPORAL_FACET_ADDRESS = address(0x456...); + +// Selector for grantRoleWithExpiry +bytes4 GRANT_ROLE_WITH_EXPIRY_SELECTOR = bytes4(keccak256("grantRoleWithExpiry(bytes32,address,uint64)")); + +// Selector for requireValidRole +bytes4 REQUIRE_VALID_ROLE_SELECTOR = bytes4(keccak256("requireValidRole(bytes32,address)")); + +// Example of granting a role with expiry +function grantTemporalRole(bytes32 _role, address _account, uint64 _expiryTimestamp) external { + (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(GRANT_ROLE_WITH_EXPIRY_SELECTOR, _role, _account, _expiryTimestamp)); + require(success, "Failed to grant temporal role"); +} + +// Example of checking for a valid role +function checkRoleValidity(bytes32 _role, address _account) external { + (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(REQUIRE_VALID_ROLE_SELECTOR, _role, _account)); + require(success, "Role is not valid or has expired"); +} +`} + + +## Best Practices + + +- Initialize roles and grant initial permissions using the `grantRoleWithExpiry` function during deployment or via an admin interface. +- Ensure the caller invoking `grantRoleWithExpiry` and `revokeTemporalRole` possesses the corresponding role's admin permission. +- Utilize `requireValidRole` before executing sensitive operations to enforce time-bound access controls. + + +## Security Considerations + + +Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role management. The `requireValidRole` function reverts if a role has expired or is not assigned, mitigating the risk of acting with stale permissions. Ensure that the `_expiryTimestamp` is set appropriately to prevent roles from remaining active indefinitely or expiring too soon. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx new file mode 100644 index 00000000..e71251c1 --- /dev/null +++ b/website/docs/contracts/facets/DiamondCutFacet.mdx @@ -0,0 +1,419 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Add=0, Replace=1, Remove=2" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Add=0, Replace=1, Remove=2 + + + +- Supports adding, replacing, and removing facets atomically. +- Allows for optional initialization of a new facet during an upgrade. +- Provides access to diamond storage layout and owner information. + + +## Overview + +The DiamondCutFacet provides the core functionality for managing facets within a Compose diamond. It enables adding new facets, replacing existing ones, and removing facets, acting as the primary interface for diamond upgrades and modifications. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +--- +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet} from "@compose-protocol/diamond-contracts/facets/DiamondCutFacet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/interfaces/IDiamondCut.sol"; + +contract DeployDiamondExample { + address internal diamondAddress; + + function deploy() external { + // Assume diamondAddress is initialized or deployed previously + diamondAddress = address(0xYourDiamondAddress); + + DiamondCutFacet diamondCutFacet = DiamondCutFacet(diamondAddress); + + // Example: Adding a new facet + bytes[] memory functionSelectors = new bytes[](1); + functionSelectors[0] = IDiamondCut.addFunctions.selector; + + address[] memory facetAddresses = new address[](1); + facetAddresses[0] = address(0xYourNewFacetAddress); + + diamondCutFacet.diamondCut( + IDiamondCut.FacetCut[] abi.encodePacked(IDiamondCut.FacetCut(facetAddresses[0], IDiamondCut.Action.Add, functionSelectors)), + address(0), // init contract + '[[\"initFunction\"]', // init calldata + diamondAddress // target diamond + ); + } +}`} + + +## Best Practices + + +- Initialize the diamond with the DiamondCutFacet as one of its initial facets to enable upgradeability from deployment. +- Use specific actions (Add, Replace, Remove) for clarity and to prevent unintended side effects during diamond upgrades. +- Store facet addresses and function selectors efficiently, considering gas costs for large numbers of facets. + + +## Security Considerations + + +Access control for `diamondCut` and related functions must be strictly enforced, typically restricted to the diamond owner or a designated admin role. Ensure that facet addresses and function selectors are validated to prevent the introduction of malicious code. Reentrancy is not a direct concern for the `diamondCut` function itself, but any `initCalldata` executed must be reentrancy-safe. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..f7f9c37c --- /dev/null +++ b/website/docs/contracts/facets/DiamondLoupeFacet.mdx @@ -0,0 +1,255 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "The functions in DiamondLoupeFacet MUST be added to a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +The functions in DiamondLoupeFacet MUST be added to a diamond. + + + +- Provides a standardized interface for querying diamond facet information. +- Enables detailed introspection of function selectors mapped to specific facet addresses. +- Optimizes memory usage for efficient retrieval of large numbers of facets and selectors. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows querying facet addresses, function selectors, and the overall facet structure of the diamond, enabling builders to understand and interact with the diamond's deployed logic. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupeFacet} from "@compose/diamond-contracts/contracts/facets/DiamondLoupeFacet.sol"; + +contract DiamondLoupeConsumer { + IDiamondLoupeFacet constant DIAMOND_LOUPe = IDiamondLoupeFacet(0x1234567890abcdef1234567890abcdef1234567890); // Replace with actual diamond address + + function getFacetAddress(bytes4 _selector) external view returns (address) { + return DIAMOND_LOUPe.facetAddress(_selector); + } + + function getAllFacetAddresses() external view returns (address[] memory) { + return DIAMOND_LOUPe.facetAddresses(); + } + + function getFacetSelectors(address _facet) external view returns (bytes4[] memory) { + return DIAMOND_LOUPe.facetFunctionSelectors(_facet); + } + + function getAllFacets() external view returns (IDiamondLoupeFacet.Facet[] memory) { + return DIAMOND_LOUPe.facets(); + } +}`} + + +## Best Practices + + +- Integrate DiamondLoupeFacet into your diamond to enable discoverability and debugging of its functionalities. +- Use the provided functions to programmatically understand the diamond's architecture, especially during upgrades or when integrating new facets. +- Cache facet addresses locally if making frequent calls to the same facet to reduce gas costs. + + +## Security Considerations + + +DiamondLoupeFacet functions are read-only and do not modify state, posing minimal direct security risks. However, the information they provide can be used to understand the diamond's attack surface. Ensure that sensitive functions are correctly protected by access control mechanisms implemented in other facets. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx new file mode 100644 index 00000000..74bb89f5 --- /dev/null +++ b/website/docs/contracts/facets/ERC1155Facet.mdx @@ -0,0 +1,679 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Supports both fungible and non-fungible tokens under a single interface. +- Provides batched operations for efficient transfers and balance checks. +- Includes URI resolution for token metadata, supporting both base and token-specific URIs. + + +## Overview + +The ERC1155Facet implements the ERC-1155 Multi-Token Standard interface. It provides functionality for managing fungible and non-fungible tokens within a Compose diamond, including token transfers, approvals, and batch operations. This facet enables a diamond to act as a comprehensive token issuer and manager. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Facet} from "@compose/diamond-contracts/facets/ERC1155/IERC1155Facet.sol"; + +contract ERC1155Consumer { + address immutable _diamondAddress; + + constructor(address diamondAddress) { + _diamondAddress = diamondAddress; + } + + function consumeERC1155() external { + IERC1155Facet erc1155Facet = IERC1155Facet(_diamondAddress); + + // Example: Check balance + uint256 balance = erc1155Facet.balanceOf(msg.sender, 1); + + // Example: Get URI + string memory uri = erc1155Facet.uri(1); + + // Example: Approve operator + erc1155Facet.setApprovalForAll(address(this), true); + } + + function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external pure override returns (bytes4) { + // Implement logic for receiving ERC1155 tokens + return + + } + + function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external pure override returns (bytes4) { + // Implement logic for receiving batched ERC1155 tokens + return + } +}`} + + +## Best Practices + + +- Ensure the `ERC1155Facet` is properly initialized with any necessary base URIs or token-specific URIs during diamond deployment. +- Implement the `onERC1155Received` and `onERC1155BatchReceived` callback functions in any contract that intends to receive ERC1155 tokens from the diamond to handle incoming transfers securely. + + +## Security Considerations + + +Input validation is crucial for token IDs and amounts to prevent unexpected behavior. Ensure that approvals granted via `setApprovalForAll` are managed carefully to avoid unintended token access. The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks to ensure the recipient contract implements the ERC1155TokenReceiver interface, mitigating reentrancy risks related to token reception. Access control for setting URIs or other administrative functions is not defined within this facet and must be managed by the diamond's access control mechanism. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..ee79db2d --- /dev/null +++ b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx @@ -0,0 +1,420 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Enables permissioned cross-chain minting and burning of ERC20 tokens. +- Leverages the diamond storage pattern for accessing ERC20 and Access Control state. +- Enforces access control via the `trusted-bridge` role for sensitive cross-chain operations. + + +## Overview + +The ERC20BridgeableFacet manages cross-chain minting and burning operations for ERC20 tokens within a Compose diamond. It provides functions to interact with ERC20 and access control storage, enabling trusted bridge addresses to perform these sensitive operations. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-protocol/diamond/contracts/interfaces/IDiamondCut.sol"; +import {AccessControlFacet} from "@compose-protocol/diamond/facets/AccessControl/AccessControlFacet.sol"; +import {ERC20BridgeableFacet} from "src/facets/ERC20BridgeableFacet.sol"; + +contract Deployer { + address diamondAddress; + + function deploy() public { + // ... Diamond deployment logic ... + // Assume diamondAddress is set after deployment + } + + function mintOnBridge(address _to, uint256 _amount) public { + ERC20BridgeableFacet erc20BridgeableFacet = ERC20BridgeableFacet(diamondAddress); + // Ensure the caller has the 'trusted-bridge' role before calling this externally + erc20BridgeableFacet.crosschainMint(_to, _amount); + } + + function burnOnBridge(address _from, uint256 _amount) public { + ERC20BridgeableFacet erc20BridgeableFacet = ERC20BridgeableFacet(diamondAddress); + // Ensure the caller has the 'trusted-bridge' role before calling this externally + erc20BridgeableFacet.crosschainBurn(_from, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is correctly assigned and managed within the AccessControlFacet before allowing external calls to `crosschainMint` and `crosschainBurn`. +- The `getERC20Storage` and `getAccessControlStorage` functions should only be called internally by other facets that require access to this specific storage. Direct external calls are not typical usage. +- Implement robust logging and monitoring for `crosschainMint` and `crosschainBurn` events to track cross-chain activity. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role, preventing unauthorized minting or burning. The `checkTokenBridge` internal function ensures that only addresses with the `trusted-bridge` role can initiate these operations, mitigating risks of unauthorized supply changes. Reentrancy is not a direct concern as these functions do not perform external calls to untrusted contracts; however, the underlying ERC20 token interaction should be considered. Input validation for `_to`, `_from`, and `_amount` is crucial to prevent underflow/overflow or minting/burning to invalid addresses. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx new file mode 100644 index 00000000..eee4f796 --- /dev/null +++ b/website/docs/contracts/facets/ERC20BurnFacet.mdx @@ -0,0 +1,260 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Contract documentation for ERC20BurnFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ERC20BurnFacet + + + +- Allows burning of ERC20 tokens directly from the diamond, reducing external dependencies. +- Supports burning from the caller's balance (`burn`) and from another account's balance via allowance (`burnFrom`). +- Emits `Transfer` events to the zero address upon successful burns, aligning with ERC20 burn conventions. + + +## Overview + +The ERC20BurnFacet provides functionality to destroy ERC20 tokens within a Compose diamond. It allows users to burn their own tokens or burn tokens from another account if they have sufficient allowance, adhering to standard ERC20 burn semantics. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20BurnFacet} from "@compose/contracts/src/facets/ERC20BurnFacet.sol"; +import {DiamondProxy} from "@compose/contracts/src/DiamondProxy.sol"; + +contract Deployer { + function deploy() external { + // Assume diamondProxy is an already deployed DiamondProxy instance + DiamondProxy diamondProxy; + + // Add the ERC20BurnFacet to the diamond + address erc20BurnFacetAddress = address(new ERC20BurnFacet()); + bytes32[] memory selectors = new bytes32[](3); + selectors[0] = ERC20BurnFacet.getStorage.selector; + selectors[1] = ERC20BurnFacet.burn.selector; + selectors[2] = ERC20BurnFacet.burnFrom.selector; + + // The diamond proxy's loupe facet would typically handle facet additions + // For demonstration, we assume a direct call to an upgrade facet + // diamondProxy.diamondCut(...); // This is a placeholder for the actual diamond cut mechanism + + // Example: Burn 100 tokens from the caller's balance + // Assuming the diamondProxy is the target and the function is routed + (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(ERC20BurnFacet.burn.selector, 100)); + require(success, "Burn failed"); + + // Example: Burn 50 tokens from another address using allowance + // (bool successFrom, bytes memory dataFrom) = address(diamondProxy).call(abi.encodeWithSelector(ERC20BurnFacet.burnFrom.selector, spenderAddress, 50)); + // require(successFrom, "Burn from failed"); + } +}`} + + +## Best Practices + + +- Ensure the `ERC20BurnFacet` is correctly added to the diamond proxy during deployment or upgrades, mapping its functions to the appropriate selectors. +- When calling `burnFrom`, verify that the caller has an adequate allowance set for the tokens being burned. + + +## Security Considerations + + +This facet does not implement complex access control beyond standard ERC20 allowance checks for `burnFrom`. Ensure that the diamond's overall access control mechanisms are robust. Reentrancy is not a direct concern for `burn` and `burnFrom` as they do not make external calls. Input validation for token amounts should be handled by the diamond proxy's routing or by the caller. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx new file mode 100644 index 00000000..74c7ffe2 --- /dev/null +++ b/website/docs/contracts/facets/ERC20Facet.mdx @@ -0,0 +1,576 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Contract documentation for ERC20Facet" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ERC20Facet + + + +- Implements the full ERC20 standard interface for fungible tokens. +- Manages token balances, total supply, and allowances. +- Supports token transfers and approvals between accounts. +- Integrates seamlessly with the Compose diamond proxy architecture. + + +## Overview + +The ERC20Facet provides standard ERC20 token functionality within a Compose diamond. It manages token metadata, balances, allowances, and transfer operations, adhering to the ERC20 standard. This facet enables fungible token capabilities directly on the diamond proxy. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamond} from "@compose/diamond-proxy/src/diamond/IDiamond.sol"; + +contract ERC20Consumer { + IDiamond public diamondProxy; + + constructor(address _diamondProxyAddress) { + diamondProxy = IDiamond(_diamondProxyAddress); + } + + function getTokenName() external view returns (string memory) { + bytes32 selector = diamondProxy.getFunctionSelector("name()", "ERC20Facet"); + (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector)); + require(success, "ERC20Consumer: failed to get token name"); + return abi.decode(data, (string)); + } + + function getTokenBalance(address _account) external view returns (uint256) { + bytes32 selector = diamondProxy.getFunctionSelector("balanceOf(address)", "ERC20Facet"); + (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector, _account)); + require(success, "ERC20Consumer: failed to get token balance"); + return abi.decode(data, (uint256)); + } + + function transferTokens(address _to, uint256 _amount) external { + bytes32 selector = diamondProxy.getFunctionSelector("transfer(address,uint256)", "ERC20Facet"); + (bool success, ) = address(diamondProxy).call(abi.encodeWithSelector(selector, _to, _amount)); + require(success, "ERC20Consumer: failed to transfer tokens"); + } +}`} + + +## Best Practices + + +- Initialize the ERC20Facet with essential metadata (name, symbol, decimals) during diamond deployment or upgrade. +- Ensure appropriate access control is configured at the diamond level for sensitive operations like approvals and transfers if required by your application's design. +- Use the `getStorage` function to retrieve the ERC20 storage struct for direct manipulation if advanced or custom ERC20 logic is needed, adhering to Compose's storage pattern. + + +## Security Considerations + + +Ensure that the `approve` and `transferFrom` functions are used with caution to prevent unintended token spending. The `transfer` and `transferFrom` functions should validate that the sender has sufficient token balance before executing. Reentrancy is mitigated by standard ERC20 patterns and the diamond proxy's internal call handling. Input validation on addresses and amounts is crucial to prevent errors. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx new file mode 100644 index 00000000..c3e25f59 --- /dev/null +++ b/website/docs/contracts/facets/ERC20PermitFacet.mdx @@ -0,0 +1,350 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "Contract documentation for ERC20PermitFacet" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ERC20PermitFacet + + + +- Implements EIP-2612 `permit` functionality, allowing off-chain approvals. +- Utilizes EIP-712 for structured, secure signature encoding. +- Provides `nonces` and `DOMAIN_SEPARATOR` for signature generation utilities. + + +## Overview + +The ERC20PermitFacet enables EIP-2612 compliant on-chain signature-based approvals for ERC20 tokens. It allows token holders to grant allowances to spenders without needing to initiate a transaction themselves, enhancing user experience and gas efficiency. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +--- +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20PermitFacet} from "@compose/diamond/facets/ERC20Permit/ERC20PermitFacet.sol"; +import {IDiamond} from "@compose/diamond/core/IDiamond.sol"; + +contract ERC20PermitConsumer { + address internal diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function consumePermit(address tokenAddress, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Assuming the ERC20PermitFacet is already deployed and attached to the diamond + // The selector for permit is keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)") + // This example assumes a direct call for simplicity, in a real scenario, + // you'd likely use the IDiamond interface or a helper function. + + // The actual call would be routed through the diamond proxy. + // For demonstration, we show the conceptual parameters. + + // address owner = ...; // The address that signed the permit + // uint256 nonce = ERC20PermitFacet(diamondAddress).nonces(owner); + // bytes32 domainSeparator = ERC20PermitFacet(diamondAddress).DOMAIN_SEPARATOR(); + + // The permit function itself handles the validation and allowance setting. + // The following is a simplified representation of the call structure. + + (bool success, ) = diamondAddress.call(abi.encodeWithSelector( + ERC20PermitFacet.permit.selector, + tokenAddress, // The address of the ERC20 token contract + spender, // The address to grant allowance to + amount, // The amount to allow + deadline, // The deadline for the permit + v, // Signature parameter v + r, // Signature parameter r + s // Signature parameter s + )); + + require(success, "Permit call failed"); + } +}`} + + +## Best Practices + + +- Ensure the `ERC20PermitFacet` is correctly initialized and attached to the diamond proxy before use. +- Use the `DOMAIN_SEPARATOR` and `nonces` functions to correctly construct EIP-712 compliant signatures for the `permit` function. +- Validate the `deadline` parameter to prevent stale permits from being used. + + +## Security Considerations + + +The `permit` function itself does not directly handle reentrancy as it primarily sets allowances. However, the underlying ERC20 token contract's `transferFrom` or similar functions, which will later consume this allowance, must be reentrancy-safe. Ensure the signature parameters (`v`, `r`, `s`) are validated correctly by the caller or a trusted off-chain service to prevent signature malleability or replay attacks. The `deadline` parameter is critical for preventing the use of expired permits. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx new file mode 100644 index 00000000..683e3642 --- /dev/null +++ b/website/docs/contracts/facets/ERC6909Facet.mdx @@ -0,0 +1,530 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements core ERC-6909 functionalities including balance, allowance, transfer, and operator management. +- Provides explicit functions for setting and querying operator status, enabling delegated permissions. +- Leverages the diamond storage pattern for efficient and upgradeable state management. + + +## Overview + +The ERC6909Facet implements the ERC-6909 standard for fungible tokens, providing essential functions for managing token balances, allowances, and operator permissions within a Compose diamond. It serves as the primary interface for ERC-6909 token interactions, orchestrating internal state changes and access control. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909 } from "@compose/contracts/interfaces/IERC6909.sol"; +import { ERC6909Facet } from "@compose/contracts/facets/ERC6909Facet.sol"; + +contract ERC6909User { + address diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getUserBalance(uint256 _tokenId) public view returns (uint256) { + IERC6909 erc6909 = IERC6909(diamondAddress); + return erc6909.balanceOf(_tokenId); + } + + function transferTokens(uint256 _tokenId, address _to, uint256 _amount) public { + IERC6909 erc6909 = IERC6909(diamondAddress); + erc6909.transfer(_tokenId, _to, _amount); + } + + function approveSpender(uint256 _tokenId, address _spender, uint256 _amount) public { + IERC6909 erc6909 = IERC6909(diamondAddress); + erc6909.approve(_tokenId, _spender, _amount); + } +}`} + + +## Best Practices + + +- Initialize the ERC6909 storage correctly during diamond deployment using the appropriate initializer function. +- Ensure proper access control is configured for functions that modify token state (e.g., `transfer`, `transferFrom`, `approve`, `setOperator`), typically managed by a separate access control facet. +- When upgrading the ERC6909Facet, ensure that the storage layout remains compatible to prevent data loss or corruption. + + +## Security Considerations + + +Functions like `transfer` and `transferFrom` are susceptible to reentrancy if not protected. Ensure that the diamond's access control facet correctly restricts calls to authorized addresses. Input validation for token IDs, amounts, and addresses is crucial to prevent unexpected behavior or exploits. Ensure the `approve` function correctly handles allowance updates, especially when setting allowances to zero or increasing existing ones. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx new file mode 100644 index 00000000..f2cb8bbc --- /dev/null +++ b/website/docs/contracts/facets/ERC721BurnFacet.mdx @@ -0,0 +1,205 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Provides an on-chain mechanism to permanently remove ERC721 tokens. +- Exposes a direct storage access function for advanced integration needs. + + +## Overview + +The ERC721BurnFacet provides the functionality to destroy ERC721 tokens within a Compose diamond. It exposes a `burn` function to remove tokens and a `getStorage` function for direct access to the underlying ERC721 storage. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "@compose/diamond-contracts/facets/ERC721/IERC721BurnFacet.sol"; + +contract ERC721BurnConsumer { + address immutable DIAMOND_ADDRESS; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function burnToken(uint256 tokenId) external { + IERC721BurnFacet(DIAMOND_ADDRESS).burn(tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Storage` struct is correctly defined and initialized before deploying this facet. +- Accessing storage directly via `getStorage` requires careful handling to avoid slot collisions or data corruption. + + +## Security Considerations + + +The `burn` function should enforce ownership checks (e.g., only the token owner or an approved address can burn) to prevent unauthorized token destruction. The `getStorage` function bypasses standard access control and requires careful usage by developers to prevent state manipulation. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..a5687c01 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,227 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Efficiently burns ERC721 tokens by removing them from the internal enumeration tracking. +- Maintains the integrity of the token index and total supply after token destruction. +- Provides a dedicated function for token burning, separate from other ERC721 operations. + + +## Overview + +The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens while ensuring that the enumeration tracking is correctly updated. It allows for the removal of tokens from the diamond's state, maintaining the integrity of the enumerable list of tokens. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Enumerable, IERC721Burn} from "@compose/contracts/src/facets/ERC721/interfaces/IERC721Enumerable.sol"; +import {ERC721EnumerableFacet} from "@compose/contracts/src/facets/ERC721/ERC721EnumerableFacet.sol"; + +contract ExampleUsage { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnToken(uint256 _tokenId) external { + IERC721Burn(diamondAddress).burn(_tokenId); + } + + function getFacetStorage() external view returns (ERC721EnumerableFacet.Layout memory) { + return ERC721EnumerableFacet.getStorage(diamondAddress); + } +}`} + + +## Best Practices + + +- Ensure the ERC721EnumerableBurnFacet is correctly registered with the Diamond Loupe facet for discoverability. +- When upgrading, ensure the storage layout compatibility is maintained to prevent data corruption. +- The `burn` function should only be called by authorized entities as defined by the diamond's access control. + + +## Security Considerations + + +The `burn` function should be protected by appropriate access control mechanisms to prevent unauthorized token destruction. Ensure that the `_tokenId` provided exists and is owned by the caller or an authorized entity before burning. Reentrancy is not a direct concern for this function, as it primarily modifies internal state and does not make external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..e940ef04 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx @@ -0,0 +1,764 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Full ERC721 compliance with added enumerable features (total supply, ownership indexing). +- Supports metadata retrieval via `tokenURI` for NFTs. +- Provides internal transfer logic (`internalTransferFrom`) for composability with other facets. +- Includes standard ERC721 approval mechanisms. + + +## Overview + +The ERC721EnumerableFacet provides the core functionality for an ERC721 Non-Fungible Token standard, including ownership tracking, approvals, and metadata retrieval. It extends ERC721 by adding enumerable properties like total supply and token ownership indexing, crucial for indexing and displaying token collections within a Compose diamond. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; +import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; + +contract DeployERC721Diamond { + address internal diamondCutAddress; + + function deploy() external { + // Assume diamondCutAddress is set or fetched + IDiamondCut diamondCut = IDiamondCut(diamondCutAddress); + + // Facet addresses would be deployed separately and referenced here + address erc721Facet = address(0x...) ; // Replace with actual deployed ERC721EnumerableFacet address + + // Define selectors for ERC721EnumerableFacet + bytes4[] memory selectors = new bytes4[](16); + selectors[0] = IERC721EnumerableFacet.getStorage.selector; + selectors[1] = IERC721EnumerableFacet.name.selector; + selectors[2] = IERC721EnumerableFacet.symbol.selector; + selectors[3] = IERC721EnumerableFacet.tokenURI.selector; + selectors[4] = IERC721EnumerableFacet.totalSupply.selector; + selectors[5] = IERC721EnumerableFacet.balanceOf.selector; + selectors[6] = IERC721EnumerableFacet.ownerOf.selector; + selectors[7] = IERC721EnumerableFacet.tokenOfOwnerByIndex.selector; + selectors[8] = IERC721EnumerableFacet.getApproved.selector; + selectors[9] = IERC721EnumerableFacet.isApprovedForAll.selector; + selectors[10] = IERC721EnumerableFacet.approve.selector; + selectors[11] = IERC721EnumerableFacet.setApprovalForAll.selector; + selectors[12] = IERC721EnumerableFacet.transferFrom.selector; + selectors[13] = IERC721EnumerableFacet.safeTransferFrom.selector; + selectors[14] = IERC721EnumerableFacet.safeTransferFrom.selector; // Overload for data + selectors[15] = IERC721EnumerableFacet.internalTransferFrom.selector; // Internal use + + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: erc721Facet, + action: IDiamondCut.FacetCutAction.ADD, + functionSelectors: selectors + }); + + // Execute the diamond cut to add the ERC721EnumerableFacet + // diamondCut.diamondCut(cut, address(0), ""); // Owner or authorized caller + } +} + +// Example of calling functions after deployment: +// IERC721EnumerableFacet nftContract = IERC721EnumerableFacet(diamondAddress); +// uint256 supply = nftContract.totalSupply(); +// address owner = nftContract.ownerOf(tokenId); +// nftContract.transferFrom(from, to, tokenId);`} + + +## Best Practices + + +- Initialize the facet with a name, symbol, and potentially a base URI during the diamond deployment process. +- Ensure access control for functions like `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` is handled by the diamond's access control facet. +- When upgrading or replacing this facet, ensure the storage layout remains compatible to avoid data loss or corruption, especially for token ownership mappings and enumerable lists. + + +## Security Considerations + + +Input validation for token IDs and addresses is critical and should be enforced by the facet's internal logic or an upstream access control mechanism. Reentrancy risks are mitigated by the standard ERC721 transfer patterns, but external calls (e.g., `tokenURI` resolvers or receiver contract callbacks in `safeTransferFrom`) must be carefully managed by the diamond's overall security design. The internal `transferFrom` function should only be callable by trusted internal facets or the diamond proxy itself to prevent unauthorized token movements. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx new file mode 100644 index 00000000..c1233344 --- /dev/null +++ b/website/docs/contracts/facets/ERC721Facet.mdx @@ -0,0 +1,668 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements the core ERC-721 standard for NFTs. +- Supports token metadata retrieval (name, symbol, tokenURI). +- Manages token ownership, balances, and approvals. +- Includes safe transfer mechanisms to prevent incompatible receiver issues. + + +## Overview + +The ERC721Facet provides a standard ERC-721 compliant interface for managing non-fungible tokens within a Compose diamond. It orchestrates token metadata, ownership, approvals, and transfers, serving as the primary surface area for ERC-721 interactions. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose/diamond/facets/ERC721Facet/IERC721Facet.sol"; +import {DiamondProxy} from "@compose/diamond/DiamondProxy.sol"; + +contract ERC721Consumer { + IERC721Facet public erc721Facet; + + constructor(address diamondProxyAddress) { + erc721Facet = IERC721Facet(diamondProxyAddress); + } + + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); + } + + function getTokenSymbol() external view returns (string memory) { + return erc721Facet.symbol(); + } + + function getOwnerOfToken(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); + } + + function transferToken(address from, address to, uint256 tokenId) external { + // Ensure caller has approval or is the owner + erc721Facet.transferFrom(from, to, tokenId); + } +}`} + + +## Best Practices + + +- Initialize the ERC721Facet with the correct storage slot via `getStorage()` for proper state management. +- Ensure proper access control for functions like `approve` and `setApprovalForAll` is handled by the diamond's access control facet. +- When upgrading, ensure that the storage layout remains compatible to prevent data corruption. + + +## Security Considerations + + +Access control for `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` should be enforced by the diamond's access control mechanism. The `internalTransferFrom` function performs critical ownership and approval checks; ensure it's called correctly by other facets or the diamond proxy. Reentrancy is mitigated by the diamond proxy pattern and the internal checks within `internalTransferFrom`. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx new file mode 100644 index 00000000..0272e287 --- /dev/null +++ b/website/docs/contracts/facets/ExampleDiamond.mdx @@ -0,0 +1,138 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Contract documentation for ExampleDiamond" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Contract documentation for ExampleDiamond + + + +- Acts as the central dispatcher for all external calls to the diamond. +- Manages the registration and unregistration of facets and their associated function selectors. +- Facilitates diamond initialization by setting the owner and adding initial facets. + + +## Overview + +The ExampleDiamond contract serves as the core of a Compose diamond. It acts as the central router, directing all external calls to the appropriate facet based on function selectors. This contract also manages diamond initialization, including setting the owner and registering facets. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ExampleDiamond} from "./ExampleDiamond.sol"; +import {DiamondCutFacet} from "@compose/diamond-core/facets/DiamondCutFacet.sol"; +import {DiamondLoupeFacet} from "@compose/diamond-core/facets/DiamondLoupeFacet.sol"; + +contract DeployExampleDiamond { + address public diamondAddress; + + function deploy() public { + // Example facet addresses and their selectors + address diamondCutFacet = address(new DiamondCutFacet()); + address diamondLoupeFacet = address(new DiamondLoupeFacet()); + + // Define facet cuts for initialization + ExampleDiamond.FacetCut[] memory facets = new ExampleDiamond.FacetCut[](2); + facets[0] = ExampleDiamond.FacetCut({ + facetAddress: diamondCutFacet, + action: ExampleDiamond.FacetCutAction.Add, + functionSelectors: DiamondCutFacet.getFunctionSelectors() + }); + facets[1] = ExampleDiamond.FacetCut({ + facetAddress: diamondLoupeFacet, + action: ExampleDiamond.FacetCutAction.Add, + functionSelectors: DiamondLoupeFacet.getFunctionSelectors() + }); + + // Deploy the diamond and initialize it + ExampleDiamond exampleDiamond = new ExampleDiamond(facets, msg.sender); + diamondAddress = address(exampleDiamond); + } +}`} + + +## Best Practices + + +- Initialize the diamond with essential facets like DiamondCutFacet and DiamondLoupeFacet during deployment. +- Ensure the owner is set correctly during initialization, as they will have the authority to manage facets. +- Use explicit `FacetCutAction` values (Add, Replace, Remove) for clarity and to prevent unintended state changes. + + +## Security Considerations + + +The constructor of ExampleDiamond sets the owner and registers facets. Ensure the owner address is trusted. Incorrectly configured facet cuts during initialization can lead to unexpected routing or loss of functionality. Access to `DiamondCutFacet` functions is typically restricted to the owner, preventing unauthorized modifications to the diamond's facet configuration. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx new file mode 100644 index 00000000..8cffb631 --- /dev/null +++ b/website/docs/contracts/facets/OwnerFacet.mdx @@ -0,0 +1,210 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Provides clear ownership management for administrative control. +- Supports transferring ownership and renouncing it to a zero address. + + +## Overview + +The OwnerFacet manages ownership of the diamond proxy. It provides functions to retrieve the current owner, transfer ownership to a new address, and renounce ownership entirely. This facet is crucial for controlling administrative actions within the diamond. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; + +contract Deployer { + // Assume diamondAbi is the Application Binary Interface of the diamond proxy + // Assume diamondAddress is the address of the deployed diamond proxy + + IOwnerFacet ownerFacet = IOwnerFacet(diamondAddress); + + function getDiamondOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function transferDiamondOwnership(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function renounceDiamondOwnership() external { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize ownership during diamond deployment to a trusted address. +- Only the current owner should call `transferOwnership` or `renounceOwnership`. +- Store the `OwnerFacet`'s storage slot in a constant to ensure predictable access. + + +## Security Considerations + + +Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Ensure the `owner` address is managed securely to prevent unauthorized control of the diamond. Setting the owner to `address(0)` effectively removes ownership, so use `renounceOwnership` with caution. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..ca9d7d4b --- /dev/null +++ b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx @@ -0,0 +1,291 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements a secure two-step ownership transfer mechanism. +- Allows for explicit acceptance of ownership, preventing accidental transfers. +- Provides functions to renounce ownership, returning the diamond to a state with no owner. + + +## Overview + +The OwnerTwoStepsFacet manages ownership of a diamond, implementing a two-step transfer process to enhance security. It provides functions to view the current and pending owner, initiate a transfer, and for the pending owner to accept or renounce the ownership. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +--- +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "./interfaces/IOwnerTwoStepsFacet.sol"; + +contract OwnerTwoStepsExample { + IOwnerTwoStepsFacet ownerFacet; + + constructor(address _diamondAddress) { + ownerFacet = IOwnerTwoStepsFacet(_diamondAddress); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function transferDiamondOwnership(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function acceptDiamondOwnership() external { + ownerFacet.acceptOwnership(); + } + + function renounceDiamondOwnership() external { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize ownership transfers by calling `transferOwnership` with the desired new owner's address. +- The new owner must explicitly accept ownership by calling `acceptOwnership` to finalize the transfer. +- Store ownership-related data using `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` to ensure correct state management. + + +## Security Considerations + + +Access control for `transferOwnership`, `acceptOwnership`, and `renounceOwnership` is implicitly handled by the diamond's access control mechanism (e.g., EIP-2565). Ensure that only authorized entities can call these functions. The `owner()` and `pendingOwner()` functions are view functions and do not pose direct security risks. Direct access to storage pointers via `getOwnerStorage` and `getPendingOwnerStorage` should be done with extreme caution to avoid unintended state modifications. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx new file mode 100644 index 00000000..f8b0a016 --- /dev/null +++ b/website/docs/contracts/facets/RoyaltyFacet.mdx @@ -0,0 +1,196 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "**Title:**" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +**Title:** + + + +- Implements the ERC-2981 `royaltyInfo` function for standardized royalty queries. +- Supports token-specific royalty overrides on top of a default royalty configuration. +- Utilizes inline assembly for efficient access to its designated storage slot. + + +## Overview + +The RoyaltyFacet manages and exposes royalty information for NFTs within a Compose diamond. It implements the ERC-2981 standard, providing a standardized way to query royalty details for specific tokens and sale prices, ensuring creators receive their due compensation. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamond} from "@compose/diamond/contracts/IDiamond.sol"; +import {IRoyaltyFacet} from "@compose/diamond/facets/RoyaltyFacet/IRoyaltyFacet.sol"; + +contract RoyaltyConsumer { + IDiamond public diamond; + + constructor(address _diamondAddress) { + diamond = IDiamond(_diamondAddress); + } + + function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 feeBasisPoints) { + // Route to the RoyaltyFacet's royaltyInfo function + (bool success, bytes memory data) = diamond.diamondCut( + diamond.getFacetFunctionSelector(IRoyaltyFacet.royaltyInfo.selector), + abi.encodeCall(IRoyaltyFacet.royaltyInfo, (_tokenId, _salePrice)) + ); + require(success, "RoyaltyConsumer: Failed to get royalty info"); + (receiver, feeBasisPoints) = abi.decode(data, (address, uint256)); + return (receiver, feeBasisPoints); + } +}`} + + +## Best Practices + + +- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. +- Ensure the correct access control is in place if functions for setting default or token-specific royalties are implemented in other facets or a dedicated admin facet. +- When upgrading or adding facets, ensure the royalty storage slot (STORAGE_POSITION) remains consistent to avoid data loss. + + +## Security Considerations + + +The `royaltyInfo` function itself is read-only and poses no direct reentrancy risk. However, ensure that any facets responsible for *setting* royalty information (default or token-specific) implement robust access control and input validation to prevent unauthorized modifications. State coupling is minimal, as this facet primarily reads its own storage and does not interact with external state beyond what's necessary for its read operations. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx new file mode 100644 index 00000000..87153323 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlMod.mdx @@ -0,0 +1,446 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Role-based access control (RBAC) module for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control (RBAC) module for Compose diamonds + + + +- Role-Based Access Control (RBAC) for granular permission management. +- Functions to grant, revoke, and check for role ownership (`grantRole`, `revokeRole`, `hasRole`). +- Built-in `requireRole` function for easy and gas-efficient access control enforcement. +- Support for setting administrative roles for other roles to manage role hierarchies. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlMod provides a robust role-based access control (RBAC) system for Compose diamonds. It enables granular permission management by defining roles and assigning them to accounts, ensuring that only authorized entities can perform sensitive operations. This module is crucial for maintaining security and integrity within a diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlMod} from "@compose/modules/AccessControlMod.sol"; + +contract MyFacet { + IAccessControlMod internal accessControlMod; + + constructor(address _accessControlModAddress) { + accessControlMod = IAccessControlMod(_accessControlModAddress); + } + + function grantAdminRole(address _account) external { + bytes32 adminRole = accessControlMod.getRoleAdmin(IAccessControlMod.DEFAULT_ADMIN_ROLE()); + accessControlMod.grantRole(adminRole, _account); + } + + function onlyAdminCanDoSomething() external { + accessControlMod.requireRole(IAccessControlMod.DEFAULT_ADMIN_ROLE()); + // ... perform admin action ... + } +}`} + + +## Best Practices + + +- Use `requireRole` to enforce access control checks directly within your facet functions, reverting with a specific error if the caller lacks the necessary role. +- Grant and revoke roles using `grantRole` and `revokeRole` respectively, ensuring proper administrative oversight for role assignments. +- Be mindful of role hierarchy by using `setRoleAdmin` to define which roles can manage other roles, preventing unintended privilege escalation. + + +## Integration Notes + + +The AccessControlMod manages its state within its own storage, which is accessible via the diamond proxy. Facets interact with AccessControlMod through its interface. When calling functions like `grantRole`, `revokeRole`, or `requireRole`, the AccessControlMod's internal storage is updated or read. Ensure that the AccessControlMod facet is correctly initialized and accessible within the diamond's facet registry for proper operation. Changes to role assignments are immediately reflected and enforced by the AccessControlMod. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx new file mode 100644 index 00000000..6b385443 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlPausableMod.mdx @@ -0,0 +1,387 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Role-based access control with pause functionality for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control with pause functionality for Compose diamonds + + + +- Provides granular control over role pausing and unpausing, allowing temporary suspension of specific permissions. +- Integrates seamlessly with Compose's diamond proxy pattern for modularity and upgradeability. +- Enforces role-based access control with built-in checks for paused states, preventing unauthorized actions. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlPausable module provides robust role-based access control integrated with pause functionality for Compose diamonds. It ensures that sensitive operations can be temporarily halted for specific roles, enhancing security and operational control during critical periods. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausable} from "@compose/contracts/modules/accesscontrol/IAccessControlPausable.sol"; +import {AccessControlFacet} from "@compose/contracts/facets/accesscontrol/AccessControlFacet.sol"; + +contract MyFacet is IAccessControlPausable { + // Assume access to the diamond proxy and its facets + IAccessControlPausable internal accessControlPausable; + + constructor(address _diamondProxy) { + accessControlPausable = IAccessControlPausable(_diamondProxy); + } + + function grantRoleToUser(bytes32 _role, address _user) external { + // Example of using a core AccessControl function (assuming AccessControlFacet is also implemented) + AccessControlFacet(msg.sender).grantRole(_role, _user); + } + + function pauseAdminRole() external { + // Pause the 'ADMIN_ROLE' using the module function + accessControlPausable.pauseRole(AccessControlFacet.ADMIN_ROLE); + } + + function unpauseAdminRole() external { + // Unpause the 'ADMIN_ROLE' using the module function + accessControlPausable.unpauseRole(AccessControlFacet.ADMIN_ROLE); + } + + function performAdminAction() external { + // Ensure the caller has the ADMIN_ROLE and it's not paused before proceeding + accessControlPausable.requireRoleNotPaused(AccessControlFacet.ADMIN_ROLE, msg.sender); + // ... perform admin action ... + } +}`} + + +## Best Practices + + +- Always use `requireRoleNotPaused` before executing critical functions to ensure the caller has the necessary role and that the role is not currently paused. +- Implement custom errors for specific access control or pause-related failures to provide clearer revert reasons to users. +- When upgrading, ensure the `AccessControlPausableMod` facet is properly registered and its storage is compatible with previous versions. + + +## Integration Notes + + +This module relies on the diamond storage pattern. The `getAccessControlStorage` function returns storage related to the underlying access control mechanisms, while `getStorage` provides access to the specific state managed by `AccessControlPausable`. Facets interacting with this module should be aware of these storage layouts to correctly interpret and modify state. The ordering of storage variables within the `AccessControlPausable` struct is critical for compatibility during upgrades. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..b4e436a0 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlTemporalMod.mdx @@ -0,0 +1,480 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Time-limited role-based access control for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Time-limited role-based access control for Compose diamonds + + + +- Grants roles with a specific expiry timestamp. +- Checks for both role assignment and non-expiry status. +- Provides granular control over temporary access permissions. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides time-limited role-based access control for Compose diamonds. It allows granting roles that automatically expire after a specified timestamp, enhancing security and manageability by enforcing temporary permissions. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; +import {AccessControlUnauthorizedAccount, AccessControlRoleExpired} from "@compose/modules/access-control-temporal/Errors.sol"; + +contract MyFacet { + IAccessControlTemporalMod internal accessControlTemporalMod; + + // Assume accessControlTemporalMod is initialized elsewhere, e.g., in the diamond deployer + + function _grantAdminRoleTemporary(address _user, uint64 _expiry) internal { + accessControlTemporalMod.grantRoleWithExpiry(keccak256("ADMIN_ROLE"), _user, _expiry); + } + + function _requireAdmin(address _user) internal { + try accessControlTemporalMod.requireValidRole(keccak256("ADMIN_ROLE"), _user) { + // Role is valid, proceed + } catch AccessControlUnauthorizedAccount { + revert("User does not have the ADMIN_ROLE"); + } catch AccessControlRoleExpired { + revert("ADMIN_ROLE has expired for this user"); + } + } +}`} + + +## Best Practices + + +- Use `requireValidRole` to enforce both role existence and non-expiry before critical operations. +- Handle `AccessControlUnauthorizedAccount` and `AccessControlRoleExpired` errors explicitly in your facets to provide clear user feedback. +- Carefully manage expiry timestamps to ensure roles are revoked in a timely manner, preventing stale permissions. + + +## Integration Notes + + +This module interacts with Compose's diamond storage pattern. Facets can access its storage and functions via the `IAccessControlTemporalMod` interface. The `grantRoleWithExpiry` and `revokeTemporalRole` functions directly modify the temporal role assignments within the diamond's state. The `requireValidRole` function reads this state to enforce access control checks, reverting if the role is unassigned or expired. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx new file mode 100644 index 00000000..e87bee2f --- /dev/null +++ b/website/docs/contracts/modules/DiamondCutMod.mdx @@ -0,0 +1,345 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Diamond upgrade (cut) module for ERC-2535 diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond upgrade (cut) module for ERC-2535 diamonds + + + +- All functions are `internal` for use in custom facets +- Follows diamond storage pattern (EIP-8042) +- Compatible with ERC-2535 diamonds +- No external dependencies or `using` directives + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +Diamond upgrade (cut) module for ERC-2535 diamonds + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Integration Notes + + +This module accesses shared diamond storage, so changes made through this module are immediately visible to facets using the same storage pattern. All functions are internal as per Compose conventions. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx new file mode 100644 index 00000000..82201456 --- /dev/null +++ b/website/docs/contracts/modules/DiamondMod.mdx @@ -0,0 +1,241 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Diamond Library - Internal functions and storage for diamond proxy functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond Library - Internal functions and storage for diamond proxy functionality. + + + +- Facet Management: Enables adding facets and their associated function selectors to the diamond during deployment. +- Function Dispatch: Provides the fallback mechanism to route external calls to the correct facet implementation. +- Internal Storage Access: Offers a way to inspect diamond storage slots internally. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +DiamondMod provides core internal functions for managing diamond proxy facets and handling function calls. It is essential for the diamond's runtime logic, enabling facet registration and dispatch, crucial for composability and upgradeability within the Compose framework. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondMod} from "@compose/diamond/contracts/diamond/IDiamondMod.sol"; + +contract ExampleFacet { + IDiamondMod internal diamondMod; + + // Assume diamondMod is initialized elsewhere, e.g., during deployment + constructor(address _diamondMod) { + diamondMod = IDiamondMod(_diamondMod); + } + + /** + * @notice Example of interacting with diamondMod to get storage. + * @dev This function is for demonstration purposes only. + */ + function exampleGetStorage() external view returns (bytes32 storageValue) { + // Example: retrieve a specific storage slot. Replace '0x...' with the actual slot. + // Note: This is a simplified example; direct storage access should be done with caution. + storageValue = diamondMod.getStorage(address(this), bytes32(uint256(0))); // Placeholder slot + return storageValue; + } +}`} + + +## Best Practices + + +- Use `addFacets` only during initial diamond deployment to prevent runtime modification of the diamond's function-to-facet mappings. +- Ensure `diamondFallback` is correctly implemented and called by the diamond proxy to route calls to the appropriate facets. +- Access storage via `getStorage` with caution, understanding that storage layout and slot allocation are critical for diamond integrity. + + +## Integration Notes + + +DiamondMod interacts directly with the diamond proxy's storage. The `addFacets` function is intended for initial deployment only and modifies the internal mapping of function selectors to facet addresses. `diamondFallback` acts as the central dispatcher, looking up the facet for a given function selector and executing it. `getStorage` allows internal access to the diamond's storage slots. Changes made via `addFacets` are persistent and define the diamond's runtime behavior. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx new file mode 100644 index 00000000..2f621add --- /dev/null +++ b/website/docs/contracts/modules/ERC1155Mod.mdx @@ -0,0 +1,618 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens. + + + +- Supports both single token and batch operations for minting, burning, and transfers, optimizing gas usage for multiple operations. +- Implements receiver validation for contract addresses during minting and transfers, ensuring adherence to the ERC1155 safe transfer mechanism. +- Allows setting a base URI and token-specific URIs, enabling rich metadata representation for ERC1155 tokens. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod provides a robust implementation of the ERC-1155 standard, enabling the management of fungible and non-fungible tokens within a Compose diamond. It facilitates token minting, burning, and transfers while adhering to safety checks for contract recipients, crucial for composability and secure asset handling. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {ERC1155Mod} from "./ERC1155Mod.sol"; // Assuming ERC1155Mod is deployed as a facet + +contract MyFacet is IERC1155Receiver { + address diamondProxy; // Address of the Compose diamond + + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; + } + + function mintNewTokens(address _to, uint256 _id, uint256 _amount) external { + // Call the mint function from the ERC1155Mod facet + ERC1155Mod(diamondProxy).mint(_to, _id, _amount); + } + + function transferMyTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + // Call the safeTransferFrom function from the ERC1155Mod facet + ERC1155Mod(diamondProxy).safeTransferFrom(_from, _to, _id, _amount, ""); + } + + // Implement IERC1155Receiver functions for contract interaction + function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) { + // Handle received ERC1155 tokens + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) { + // Handle received batch of ERC1155 tokens + return this.onERC1155BatchReceived.selector; + } +}`} + + +## Best Practices + + +- Always validate the `_to` address in `mint` and `mintBatch` functions when it is a contract to ensure it implements `IERC1155Receiver` for safe transfers. +- Use `safeTransferFrom` and `safeBatchTransferFrom` for all external transfers to ensure receiver contract adherence to the ERC1155 standard. +- When overriding or extending functionality, ensure calls to `burn`, `burnBatch`, `mint`, and `mintBatch` correctly update balances and emit the appropriate `TransferSingle` or `TransferBatch` events. + + +## Integration Notes + + +The ERC1155Mod interacts with a predefined diamond storage slot for its state, including token balances, approvals, and URI configurations. Facets can access this storage via the `getStorage` function. Any changes made to the storage by the ERC1155Mod functions (e.g., balance updates, URI settings) are directly reflected in the diamond's state and are visible to all facets. The order of storage variables within the ERC1155 storage struct must be maintained when composing with other modules to avoid state corruption. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx new file mode 100644 index 00000000..10255b35 --- /dev/null +++ b/website/docs/contracts/modules/ERC165Mod.mdx @@ -0,0 +1,158 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. + + + +- Implements the ERC-165 standard for interface detection. +- Provides a dedicated storage slot for interface support tracking. +- Enables facets to programmatically declare supported interfaces. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod provides the necessary logic and storage for implementing the ERC-165 standard within a Compose diamond. This enables contracts to programmatically report their supported interfaces, enhancing interoperability and discoverability for diamond facets. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC165Mod} from "@compose/diamond/modules/ERC165/IERC165Mod.sol"; + +contract MyERC721Facet { + struct Storage { + // ... other storage variables + IERC165Mod.ERC165Storage erc165Storage; + } + + function initialize(Storage storage self, address _diamondAddress) external { + // Register ERC721 interface during facet initialization + IERC165Mod.registerInterface(self.erc165Storage, type(IERC721).interfaceId); + } + + function supportsInterface(bytes4 interfaceId) external view returns (bool) { + // Delegate to the ERC165Mod logic + return IERC165Mod.supportsInterface(erc165Storage, interfaceId); + } +}`} + + +## Best Practices + + +- Ensure `registerInterface` is called during facet initialization to correctly register supported interfaces. +- Access the ERC165 storage via `getStorage` to ensure correct slot binding for interface detection. + + +## Integration Notes + + +The ERC165Mod utilizes a dedicated storage struct, `ERC165Storage`, which must be included in the facet's storage layout. The `getStorage` function, using inline assembly, binds this struct to its designated storage slot, ensuring that `supportsInterface` queries correctly retrieve interface data. It is crucial to call `registerInterface` during facet initialization to populate this storage with the interfaces the facet supports. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..f0d6efcd --- /dev/null +++ b/website/docs/contracts/modules/ERC20BridgeableMod.mdx @@ -0,0 +1,439 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "LibERC20Bridgeable — ERC-7802 Library" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Bridgeable — ERC-7802 Library + + + +- Enforces `trusted-bridge` role for cross-chain minting and burning operations, enhancing security. +- Provides helper functions (`getERC20Storage`, `getAccessControlStorage`) for accessing module-specific storage via diamond slots. +- Designed for integration into Compose diamonds, leveraging the diamond storage pattern for modularity. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20BridgeableMod module provides functionality for cross-chain ERC20 token operations. It enables secure minting and burning of tokens across different blockchains by enforcing access control for trusted bridge operators. This module is crucial for maintaining the integrity and security of cross-chain token transfers within a Compose diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableMod} from "./interfaces/IERC20BridgeableMod.sol"; + +contract MyERC20Facet { + address constant ERC20_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.erc20")))); + address constant ACCESS_CONTROL_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.accessControl")))); + + IERC20BridgeableMod private erc20BridgeableMod; + + function initialize(address _diamondAddress) external { + erc20BridgeableMod = IERC20BridgeableMod(_diamondAddress); + } + + /** + * @notice Burns ERC20 tokens for a cross-chain operation. + * @param _token The address of the ERC20 token. + * @param _from The address from which tokens are burned. + * @param _amount The amount of tokens to burn. + */ + function burnForBridge(address _token, address _from, uint256 _amount) external { + // Assume caller is a trusted bridge operator, enforced by the module. + erc20BridgeableMod.crosschainBurn(_token, _from, _amount); + } + + /** + * @notice Mints ERC20 tokens for a cross-chain operation. + * @param _token The address of the ERC20 token. + * @param _to The address to which tokens are minted. + * @param _amount The amount of tokens to mint. + */ + function mintFromBridge(address _token, address _to, uint256 _amount) external { + // Assume caller is a trusted bridge operator, enforced by the module. + erc20BridgeableMod.crosschainMint(_token, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure that only authorized addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint` functions. The module enforces this, but proper role management is essential. +- When integrating, be mindful of the storage slot assignments for ERC20 and AccessControl data, as specified by the module. Deviating can lead to incorrect behavior. +- Handle potential reverts from `checkTokenBridge` gracefully in calling facets, although the module's internal checks aim to prevent this by design. + + +## Integration Notes + + +This module interacts with the diamond's storage to manage ERC20 token states and access control roles. The `getERC20Storage` and `getAccessControlStorage` functions retrieve references to these storage areas using predefined diamond storage slots. Facets integrating with this module will call its functions, which in turn perform checks against the AccessControl storage to verify the caller's `trusted-bridge` role before executing mint or burn operations. Ensure that the diamond's storage layout is compatible with the slots expected by this module. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx new file mode 100644 index 00000000..c911ed4b --- /dev/null +++ b/website/docs/contracts/modules/ERC20Mod.mdx @@ -0,0 +1,429 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic. + + + +- Provides `mint`, `burn`, `transfer`, and `transferFrom` internal functions for standard ERC-20 operations. +- Manages ERC-20 token balances and total supply. +- Includes an `approve` function to manage spender allowances. +- Utilizes `getStorage` to provide a reference to the ERC-20 specific storage slot. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod module provides essential internal functions and storage for ERC-20 token logic within a Compose diamond. It enables standard ERC-20 operations like minting, burning, transferring, and approving allowances, ensuring composability and adherence to the ERC-20 standard. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "./interfaces/IERC20Mod.sol"; +import { ERC20Storage } from "./storage/ERC20Storage.sol"; + +contract ERC20Facet { + // Assume ERC20Storage is correctly laid out in the diamond's storage + // and IERC20Mod is the interface for the ERC20Mod library. + + function mintTokens(address _to, uint256 _amount) external { + // Obtain a pointer to the ERC20 storage struct + ERC20Storage storage erc20 = IERC20Mod.getStorage(); + + // Call the internal mint function from the ERC20Mod library + IERC20Mod.mint(erc20, address(this), _to, _amount); + } + + function transferTokens(address _from, address _to, uint256 _amount) external { + ERC20Storage storage erc20 = IERC20Mod.getStorage(); + IERC20Mod.transfer(erc20, _from, _to, _amount); + } + + function approveAllowance(address _spender, uint256 _amount) external { + ERC20Storage storage erc20 = IERC20Mod.getStorage(); + IERC20Mod.approve(erc20, msg.sender, _spender, _amount); + } +}`} + + +## Best Practices + + +- Access control for minting and burning should be enforced by the calling facet, as ERC20Mod provides only the core logic. +- Ensure the `ERC20Storage` struct is correctly initialized and laid out in the diamond's storage to prevent state corruption. +- Handle potential `ERC20TransferFailed` or other custom errors gracefully in your facet logic. + + +## Integration Notes + + +The ERC20Mod relies on a specific storage slot for its `ERC20Storage` struct. Facets using this module must ensure that the diamond's storage layout correctly allocates this slot and that the `ERC20Storage` struct definition (including variable order and types) is identical to the one used internally by ERC20Mod. The `getStorage` function uses inline assembly to reliably access this storage position. Changes to the `ERC20Storage` struct definition or its storage slot will break compatibility and require diamond upgrades. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx new file mode 100644 index 00000000..15d23eb0 --- /dev/null +++ b/website/docs/contracts/modules/ERC20PermitMod.mdx @@ -0,0 +1,309 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage" +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage + + + +- Implements ERC-2612 permit logic, enabling gas-efficient off-chain token approvals. +- Provides a self-contained domain separator calculation, preventing signature replay attacks across different contracts or chains. +- Designed for integration into Compose diamonds, leveraging the diamond storage pattern for state management. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20PermitMod library provides self-contained logic and storage for implementing ERC-2612 permit functionality within a Compose diamond. This enables gas-efficient, off-chain approvals for ERC-20 token transfers, enhancing composability and user experience by reducing on-chain transaction costs for approvals. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20PermitMod, ERC20PermitMod} from "@compose-protocol/contracts/src/modules/ERC20PermitMod.sol"; +import {DiamondStorage} from "@compose-protocol/contracts/src/DiamondStorage.sol"; + +contract MyERC20Facet { + using ERC20PermitMod for ERC20PermitMod.PermitStorage; + + DiamondStorage internal diamondStorage; + + /** + * @notice Approves a spender to withdraw tokens on behalf of the owner using a permit signature. + * @dev This function must be implemented by the facet calling the ERC20PermitMod library. + * @param _owner The address of the token owner. + * @param _spender The address to be approved. + * @param _value The amount of tokens to be approved. + * @param _deadline The deadline for the permit. + * @param _v The v component of the signature. + * @param _r The r component of the signature. + * @param _s The s component of the signature. + */ + function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { + // Ensure the facet has access to the diamond storage. + // The actual storage access will depend on the Compose diamond implementation. + ERC20PermitMod.PermitStorage storage permitStorage = _getPermitStorage(); + + // Call the permit logic from the library. + permitStorage.permit(_owner, _spender, _value, _deadline, _v, _r, _s); + } + + /** + * @notice Retrieves the permit storage for the ERC20PermitMod. + * @return permitStorage The storage struct for permit logic. + */ + function _getPermitStorage() internal view returns (ERC20PermitMod.PermitStorage storage) { + // This is a placeholder. Actual storage retrieval depends on the diamond's storage layout. + // It would typically involve accessing a specific slot in the diamond storage. + return ERC20PermitMod.getPermitStorage(diamondStorage); + } + + /** + * @notice Returns the domain separator used in the encoding of the signature for {permit}. + * @return The domain separator. + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + ERC20PermitMod.PermitStorage storage permitStorage = _getPermitStorage(); + return permitStorage.DOMAIN_SEPARATOR(); + } +} +`} + + +## Best Practices + + +- Ensure the `permit` function in your facet correctly emits the `Approval` event after calling the library function, as the library itself does not emit events. +- Validate the `_deadline` parameter to prevent expired permits from being used and consider using a sufficiently distant deadline to accommodate off-chain signing and on-chain execution delays. +- Carefully manage access control for the `permit` function to ensure only authorized entities can execute permit approvals. + + +## Integration Notes + + +The ERC20PermitMod library manages its own state through a `PermitStorage` struct. Facets integrating this module must correctly retrieve and pass this storage slot to the library functions. The `getPermitStorage` function within the library is responsible for accessing this state, which is assumed to be managed within the diamond's overall storage layout. Facets are responsible for calling the `permit` function and ensuring the associated `Approval` event is emitted externally. The domain separator is calculated based on the contract's address and chain ID, ensuring uniqueness for replay protection. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx new file mode 100644 index 00000000..1cbd2017 --- /dev/null +++ b/website/docs/contracts/modules/ERC6909Mod.mdx @@ -0,0 +1,524 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic. + + + +- Implements the core logic for ERC-6909 minimal multi-token standard. +- Utilizes diamond storage via inline assembly for efficient state access. +- Supports standard token operations: mint, burn, transfer, and approve. +- Includes operator functionality for delegated transfers. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC6909Mod provides the core internal logic and storage for implementing the ERC-6909 minimal multi-token standard within a Compose diamond. It enables facets to manage token approvals, transfers, minting, and burning efficiently by leveraging the diamond's storage pattern. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod, IERC6909ModStorage} from "@compose-protocol/diamond-contracts/contracts/modules/ERC6909/ERC6909Mod.sol"; +import {DiamondStorage} from "@compose-protocol/diamond-contracts/contracts/core/DiamondStorage.sol"; + +contract ERC6909Facet { + using DiamondStorage for IDiamondStorage; + + function mintToken(uint256 _id, uint256 _amount, address _to) external { + address contractAddress = address(this); + contractAddress.call(abi.encodeWithSignature(\"mint(uint256,uint256,address)\", _id, _amount, _to)); + } + + function transferToken(uint256 _id, uint256 _amount, address _from, address _to) external { + address contractAddress = address(this); + contractAddress.call(abi.encodeWithSignature(\"transfer(uint256,uint256,address,address)\", _id, _amount, _from, _to)); + } + + function approveToken(uint256 _id, address _spender, uint256 _amount) external { + address contractAddress = address(this); + contractAddress.call(abi.encodeWithSignature(\"approve(uint256,address,uint256)\", _id, _spender, _amount)); + } +}`} + + +## Best Practices + + +- Ensure that access control for mint, burn, and setOperator functions is implemented at the facet level, as the module itself is permissionless. +- When interacting with the module, always use the diamond's proxy address for function calls to ensure state is managed correctly within the diamond's storage. +- Be mindful of allowance deduction logic in `transfer`. Special handling for `type(uint256).max` and operator status is crucial for correct behavior. + + +## Integration Notes + + +The ERC6909Mod relies on a specific storage slot defined by `STORAGE_POSITION` within the diamond's `DiamondStorage` struct. Facets interact with this module by calling its functions through the diamond proxy. The module directly reads and writes to the `ERC6909ModStorage` struct, which is part of the overall diamond storage. Ensure that no other facets or modules attempt to use the same storage slot to prevent state corruption. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..fe817335 --- /dev/null +++ b/website/docs/contracts/modules/ERC721EnumerableMod.mdx @@ -0,0 +1,372 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. + + + +- Manages internal token enumeration lists for ERC-721 tokens. +- Integrates seamlessly with minting, burning, and transfer operations to maintain list integrity. +- Utilizes inline assembly for efficient retrieval of its storage slot from the diamond's storage. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721EnumerableMod provides the core logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures tokens are correctly added to and removed from enumeration lists during minting, burning, and transfers, maintaining the integrity of token ownership and ordering for on-chain querying. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "./IERC721EnumerableMod.sol"; +import {ERC721Storage} from "./ERC721Storage.sol"; + +contract MyERC721Facet { + // Assume diamond storage is accessible and initialized. + // For example purposes, we'll simulate it here. + ERC721Storage internal _erc721Storage; + + // Assume this facet has access to the diamond proxy's storage layout + // and specifically the storage slot for ERC721EnumerableMod. + // The actual implementation would involve fetching this from the diamond's storage. + function _getEnumerableMod() internal view returns (IERC721EnumerableMod) { + // This is a placeholder. In a real diamond, this would fetch the + // contract address of the ERC721EnumerableMod facet. + return IERC721EnumerableMod(address(this)); // Replace with actual fetch + } + + /** + * @notice Mints a new ERC-721 token. + * @param _to The address to mint the token to. + * @param _tokenId The ID of the token to mint. + */ + function mintToken(address _to, uint256 _tokenId) external { + require(_to != address(0), "ERC721: mint to the zero address"); + // Assume ownership and approval checks are handled by a separate facet or logic. + _getEnumerableMod().mint(_to, _tokenId); + } + + /** + * @notice Transfers an ERC-721 token. + * @param _from The address to transfer the token from. + * @param _to The address to transfer the token to. + * @param _tokenId The ID of the token to transfer. + */ + function transferToken(address _from, address _to, uint256 _tokenId) external { + require(_to != address(0), "ERC721: transfer to the zero address"); + // Assume ownership and approval checks are handled by a separate facet or logic. + _getEnumerableMod().transferFrom(_from, _to, _tokenId); + } + + /** + * @notice Burns an ERC-721 token. + * @param _tokenId The ID of the token to burn. + */ + function burnToken(uint256 _tokenId) external { + // Assume ownership and approval checks are handled by a separate facet or logic. + _getEnumerableMod().burn(_tokenId); + } +} +`} + + +## Best Practices + + +- Ensure that access control for `mint`, `burn`, and `transferFrom` is enforced by the calling facet or an upstream authorization mechanism, as the module itself primarily handles state updates for enumeration. +- When upgrading facets that interact with `ERC721EnumerableMod`, verify that the storage layout remains compatible to prevent data corruption or loss. +- Handle potential reverts from the module's functions gracefully by implementing appropriate error handling in the calling facet. + + +## Integration Notes + + +The ERC721EnumerableMod interacts directly with the diamond's storage, specifically managing state within its own predefined storage slot. Facets that use this module will call its functions, and the module will directly modify the diamond's storage. The `getStorage` function demonstrates how to access this specific storage slot using inline assembly. It is crucial for facets to correctly identify and interact with this storage slot to ensure proper functioning. Any changes to the module's storage layout in future upgrades must be carefully managed to maintain backward compatibility for existing tokens and facets. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx new file mode 100644 index 00000000..4b958d74 --- /dev/null +++ b/website/docs/contracts/modules/ERC721Mod.mdx @@ -0,0 +1,365 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. + + + +- Provides internal, reusable logic for core ERC-721 operations. +- Interacts directly with diamond storage for state management. +- Enables composability by abstracting complex ERC-721 state transitions. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod module provides essential internal logic for managing ERC-721 compliant tokens within a Compose diamond. It enables facets to securely mint, burn, and transfer tokens by interacting directly with the diamond's storage, ensuring state consistency and composability. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod, IERC721Storage} from "./interfaces/IERC721Mod.sol"; +import {ERC721Mod} from "./ERC721Mod.sol"; + +contract MyERC721Facet { + address constant ERC721_MOD_SLOT = 1; // Example storage slot + + function mintToken(address _to, uint256 _tokenId) external { + ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); + erc721Mod.mint(_to, _tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); + erc721Mod.transferFrom(_from, _to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); + erc721Mod.burn(_tokenId); + } + + function getERC721Storage() internal view returns (IERC721Storage.ERC721Storage memory) { + ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); + return erc721Mod.getStorage(); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Mod` is deployed to a consistent, well-known storage slot across all facets that interact with it. +- Perform necessary access control checks within your facet before calling `ERC721Mod` functions like `mint` or `transferFrom`. +- Handle potential reverts from `ERC721Mod` functions gracefully, especially for non-existent tokens during `burn` or `transferFrom`. + + +## Integration Notes + + +The `ERC721Mod` module is designed to be called by custom facets. It accesses and modifies the diamond's storage directly via a predefined storage slot (specified by the `getStorage` function's implementation, though this is an internal detail). Facets using this module must ensure that the storage slot for `ERC721Mod` is correctly initialized and accessible. The `getStorage` function returns the ERC721 storage struct, allowing facets to read current ERC-721 state. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx new file mode 100644 index 00000000..d35fbcbc --- /dev/null +++ b/website/docs/contracts/modules/NonReentrancyMod.mdx @@ -0,0 +1,142 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. + + + +- Prevents reentrant function calls by maintaining a state flag. +- Offers `enter` and `exit` functions for explicit control over reentrancy guards. +- Designed for integration into any facet requiring protection against recursive execution. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides essential mechanisms to prevent reentrant calls within your diamond facets. By implementing `enter` and `exit` functions, you can safeguard critical operations from recursive execution, ensuring contract stability and predictable state transitions. + +--- + +## Storage + +--- +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/core/src/libraries/LibNonReentrancy.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 internal _nonReentrancyState; + + /** + * @notice Performs a sensitive operation without allowing reentrancy. + */ + function sensitiveOperation() external { + // Lock reentrancy before executing sensitive logic. + _nonReentrancyState.enter(); + + try { + // ... sensitive logic here ... + } finally { + // Ensure reentrancy is always re-enabled, even if an error occurs. + _nonReentrancyState.exit(); + } + } +}`} + + +## Best Practices + + +- Always pair `enter()` with `exit()` to prevent reentrancy. Use `try/finally` blocks to guarantee `exit()` is called, even on reverts. +- Use `enter()` at the very beginning of a function and `exit()` at the very end to ensure the entire function body is protected. +- Consider the state variable used to track reentrancy status; it should be unique per guarded function or operation. + + +## Integration Notes + + +The `LibNonReentrancy` library requires a state variable within your facet to track the reentrancy status. This variable is typically a `uint256` or similar type, where a value of `1` signifies that the function is currently executing (reentrancy locked) and `0` signifies it is available. Facets using this library should initialize this state variable to `0` and call `enter()` before executing protected logic and `exit()` immediately after. Changes to this state variable are local to the facet and do not directly interact with diamond storage beyond the facet's own storage slot. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx new file mode 100644 index 00000000..caa8b2d7 --- /dev/null +++ b/website/docs/contracts/modules/OwnerMod.mdx @@ -0,0 +1,250 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. + + + +- Implements ERC-173 ownership standard for clear contract accountability. +- Provides `requireOwner()` for straightforward access control based on contract ownership. +- Supports explicit ownership transfer via `transferOwnership()`, including ownership renouncement. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerMod module provides essential functionality for managing contract ownership according to the ERC-173 standard. It enables secure owner retrieval, owner-only access control, and ownership transfer, crucial for administrative operations within a Compose diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; + +contract MyOwnerFacet { + address immutable OWNER_MOD_STORAGE_SLOT; + + constructor(address _ownerModAddress) { + OWNER_MOD_STORAGE_SLOT = _ownerModAddress; + } + + function checkOwner() external view { + address currentOwner = IOwnerMod(OWNER_MOD_STORAGE_SLOT).owner(); + // ... use currentOwner ... + } + + function transferOwnershipToNew(address _newOwner) external { + IOwnerMod(OWNER_MOD_STORAGE_SLOT).transferOwnership(_newOwner); + } +}`} + + +## Best Practices + + +- Use `requireOwner()` judiciously to protect critical administrative functions. Ensure callers understand the implications of ownership changes. +- Handle `transferOwnership` with care, especially when setting the new owner to `address(0)` to renounce ownership. This action is irreversible. +- Be aware that `OwnerMod` relies on specific storage slot allocation. Do not alter the `STORAGE_POSITION` or storage struct layout without thorough testing and understanding of diamond upgrade implications. + + +## Integration Notes + + +The OwnerMod module utilizes a dedicated storage slot, defined by `STORAGE_POSITION`, to store its `OwnerModStorage` struct. Facets interacting with OwnerMod must be aware of this slot and use inline assembly via `getStorage()` to access the `OwnerModStorage` struct pointer. Changes to the owner are immediately reflected across all facets interacting with this storage slot. The `OwnerModStorage` struct should not be modified by other facets to maintain the integrity of the ownership mechanism. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..ad87fba8 --- /dev/null +++ b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx @@ -0,0 +1,304 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. + + + +- Implements a two-step ownership transfer process for enhanced security. +- Provides `requireOwner()` for robust access control checks within facets. +- Offers `renounceOwnership()` to permanently relinquish owner rights. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerTwoStepsMod provides a secure, two-step ownership transfer mechanism for Compose diamonds. This pattern prevents accidental or malicious ownership changes by requiring explicit acceptance from the new owner, enhancing contract safety and upgradeability. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +Storage position: `OWNER_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {OwnerTwoStepsMod} from "./OwnerTwoStepsMod.sol"; + +contract MyFacet is OwnerTwoStepsMod { + /** + * @custom:security-check requireOwner + */ + function sensitiveOperation() external { + requireOwner(); // Ensures only the owner can call this function + // ... perform sensitive operation ... + } + + function initiateOwnershipTransfer(address _newOwner) external { + transferOwnership(_newOwner); + } + + function acceptNewOwnership() external { + acceptOwnership(); // Call this when you are the pending owner + } + + function getCurrentOwner() external view returns (address) { + return owner(); + } + + function getPendingOwner() external view returns (address) { + return pendingOwner(); + } +}`} + + +## Best Practices + + +- Always use `requireOwner()` within facet functions that require administrative privileges to prevent unauthorized access. +- Implement `acceptOwnership()` in a separate transaction to complete the ownership transfer securely. +- Be aware that `renounceOwnership()` permanently removes owner privileges, so use it with extreme caution. + + +## Integration Notes + + +This module relies on specific storage slots for `Owner` and `PendingOwner`. Facets integrating this module will interact with these storage variables indirectly through the module's functions. Ensure that no other facets or modules attempt to write to these exact storage slots to maintain data integrity and prevent conflicts. The `getOwnerStorage` and `getPendingOwnerStorage` functions can be used to retrieve direct pointers to these storage locations if needed for advanced integration, but direct manipulation is discouraged. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx new file mode 100644 index 00000000..ca3c56bb --- /dev/null +++ b/website/docs/contracts/modules/RoyaltyMod.mdx @@ -0,0 +1,365 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic." +gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic. + + + +- Implements ERC-2981 standard for on-chain royalty information. +- Supports both default royalties applicable to all tokens and token-specific overrides. +- Provides a fallback mechanism to default royalties when token-specific royalties are not set. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The RoyaltyMod provides the necessary internal logic and storage structures to implement the ERC-2981 royalty standard within a Compose diamond. It enables setting and querying default and token-specific royalties, ensuring compliance and composability for NFT marketplaces and other royalty-dependent applications. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "./IRoyaltyMod.sol"; +import {RoyaltyStorage} from "./RoyaltyStorage.sol"; + +contract RoyaltyFacet { + // Assume RoyaltyMod is deployed and its address is known + address immutable royaltyModAddress; + + constructor(address _royaltyModAddress) { + royaltyModAddress = _royaltyModAddress; + } + + /** + * @notice Sets a default royalty for all tokens. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee percentage in basis points (e.g., 100 for 1%). + */ + function setDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { + IRoyaltyMod(royaltyModAddress).setDefaultRoyalty(_receiver, _feeBasisPoints); + } + + /** + * @notice Sets a specific royalty for a token. + * @param _tokenId The ID of the token. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee percentage in basis points. + */ + function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + IRoyaltyMod(royaltyModAddress).setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Queries royalty information for a given token and sale price. + * @param _tokenId The ID of the token. + * @param _salePrice The sale price of the token. + * @return receiver The address to receive royalties. + * @return feeBasisPoints The royalty fee percentage in basis points. + */ + function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint16 feeBasisPoints) { + return IRoyaltyMod(royaltyModAddress).royaltyInfo(_tokenId, _salePrice); + } +}`} + + +## Best Practices + + +- Ensure that the `setDefaultRoyalty` and `setTokenRoyalty` functions are protected by appropriate access control mechanisms to prevent unauthorized modifications. +- Always validate `_receiver` addresses and `_feeBasisPoints` to prevent zero or excessive fees being set, which could lead to unexpected behavior or gas inefficiencies. +- Be mindful of storage slot collisions if integrating custom storage for royalties; use `getStorage` to understand the current layout. + + +## Integration Notes + + +The RoyaltyMod utilizes a predefined storage slot to manage its `RoyaltyStorage` struct. Facets interacting with royalty logic must call the `getStorage` function to access this shared state. Changes made by `setDefaultRoyalty` and `setTokenRoyalty` are immediately visible to any facet calling `royaltyInfo` or `getStorage`. The storage layout of `RoyaltyStorage` should not be altered by other facets to maintain compatibility. + + +
+ +
+ + From 2cd017bcbfc77fc42eceb5b1370774e2642f5857 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 17:42:04 -0500 Subject: [PATCH 32/68] improve parsing again --- .../generate-docs-utils/pr-body-generator.js | 9 ++++---- .github/scripts/generate-docs.js | 23 ++++++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/scripts/generate-docs-utils/pr-body-generator.js b/.github/scripts/generate-docs-utils/pr-body-generator.js index fcba50e5..a85fbcdc 100644 --- a/.github/scripts/generate-docs-utils/pr-body-generator.js +++ b/.github/scripts/generate-docs-utils/pr-body-generator.js @@ -23,9 +23,9 @@ function generatePRBody(summary) { const total = summary.totalGenerated || 0; let body = '## Auto-Generated Docs Pages\n\n'; - body += 'This PR contains auto-generated documentation from contract comments using `forge doc`.'; - body += 'The output is passed through AI to enhance the documentation content and add additional information.\n'; - body += 'Please ALWAYS review the generated content and ensure it is accurate and complete to Compose Standards.\n'; + body += 'This PR contains auto-generated documentation from contract comments using `forge doc`. '; + body += 'The output is passed through AI to enhance the documentation content and add additional information.\n\n'; + body += '**Please ALWAYS review the generated content and ensure it is accurate and complete to Compose Standards.**\n'; body += '### Summary\n'; @@ -60,7 +60,8 @@ function generatePRBody(summary) { body += '- [ ] Ensure consistency with existing docs\n\n'; body += '---\n'; - body += ' 🚨 This PR was automatically generated. Please ALWAYS review before merging\n'; + body += '** 🚨 This PR was automatically generated. Please ALWAYS review before merging **\n'; + body += `Generated on: ${new Date().toISOString()}\n`; return body; } diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index 9afe2dda..f21c7c9e 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -84,6 +84,28 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { data.storageInfo = extractStorageInfo(data); } + // Apply smart description fallback for facets with generic descriptions + // (Modules are handled in processAggregatedFiles) + if (contractType === 'facet') { + // Check if description looks like an enum definition (e.g., "Add=0, Replace=1, Remove=2") + const looksLikeEnum = data.description && /\w+\s*=\s*\d+/.test(data.description) && + (data.description.match(/\w+\s*=\s*\d+/g) || []).length >= 2; + + const isGenericDescription = !data.description || + data.description.startsWith('Contract documentation for') || + looksLikeEnum || + data.description.length < 20; + + if (isGenericDescription) { + const generatedDescription = generateDescriptionFromName(data.title); + if (generatedDescription) { + data.description = generatedDescription; + data.subtitle = generatedDescription; + data.overview = generatedDescription; + } + } + } + // Check if we should skip AI enhancement (e.g., for interfaces or when SKIP_ENHANCEMENT is set) const skipAIEnhancement = shouldSkipEnhancement(data) || process.env.SKIP_ENHANCEMENT === 'true'; @@ -154,7 +176,6 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { continue; } - console.log(`Reading: ${path.basename(forgeDocFile)}`); const parsed = parseIndividualItemFile(content, forgeDocFile); if (parsed) { parsedItems.push(parsed); From 4f58223f08f652d9b72a6792643c40cac4f0f4a0 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 17:44:26 -0500 Subject: [PATCH 33/68] remove old generated pages --- .../contracts/facets/AccessControlFacet.mdx | 561 ------------- .../facets/AccessControlPausableFacet.mdx | 388 --------- .../facets/AccessControlTemporalFacet.mdx | 453 ----------- .../docs/contracts/facets/DiamondCutFacet.mdx | 419 ---------- .../contracts/facets/DiamondLoupeFacet.mdx | 255 ------ .../docs/contracts/facets/ERC1155Facet.mdx | 679 ---------------- .../contracts/facets/ERC20BridgeableFacet.mdx | 420 ---------- .../docs/contracts/facets/ERC20BurnFacet.mdx | 260 ------ website/docs/contracts/facets/ERC20Facet.mdx | 576 ------------- .../contracts/facets/ERC20PermitFacet.mdx | 350 -------- .../docs/contracts/facets/ERC6909Facet.mdx | 530 ------------ .../docs/contracts/facets/ERC721BurnFacet.mdx | 205 ----- .../facets/ERC721EnumerableBurnFacet.mdx | 227 ------ .../facets/ERC721EnumerableFacet.mdx | 764 ------------------ website/docs/contracts/facets/ERC721Facet.mdx | 668 --------------- .../docs/contracts/facets/ExampleDiamond.mdx | 138 ---- website/docs/contracts/facets/OwnerFacet.mdx | 210 ----- .../contracts/facets/OwnerTwoStepsFacet.mdx | 291 ------- .../docs/contracts/facets/RoyaltyFacet.mdx | 196 ----- .../contracts/modules/AccessControlMod.mdx | 446 ---------- .../modules/AccessControlPausableMod.mdx | 387 --------- .../modules/AccessControlTemporalMod.mdx | 480 ----------- .../docs/contracts/modules/DiamondCutMod.mdx | 345 -------- website/docs/contracts/modules/DiamondMod.mdx | 241 ------ website/docs/contracts/modules/ERC1155Mod.mdx | 618 -------------- website/docs/contracts/modules/ERC165Mod.mdx | 158 ---- .../contracts/modules/ERC20BridgeableMod.mdx | 439 ---------- website/docs/contracts/modules/ERC20Mod.mdx | 429 ---------- .../docs/contracts/modules/ERC20PermitMod.mdx | 309 ------- website/docs/contracts/modules/ERC6909Mod.mdx | 524 ------------ .../contracts/modules/ERC721EnumerableMod.mdx | 372 --------- website/docs/contracts/modules/ERC721Mod.mdx | 365 --------- .../contracts/modules/NonReentrancyMod.mdx | 142 ---- website/docs/contracts/modules/OwnerMod.mdx | 250 ------ .../contracts/modules/OwnerTwoStepsMod.mdx | 304 ------- website/docs/contracts/modules/RoyaltyMod.mdx | 365 --------- 36 files changed, 13764 deletions(-) delete mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721Facet.mdx delete mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx delete mode 100644 website/docs/contracts/facets/OwnerFacet.mdx delete mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx delete mode 100644 website/docs/contracts/modules/AccessControlMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondMod.mdx delete mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC165Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC20Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx delete mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC721Mod.mdx delete mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx deleted file mode 100644 index 54007121..00000000 --- a/website/docs/contracts/facets/AccessControlFacet.mdx +++ /dev/null @@ -1,561 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Contract documentation for AccessControlFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for AccessControlFacet - - - -- Role-Based Access Control (RBAC): Define custom roles and assign them to addresses. -- Granular Permissions: Grant and revoke roles individually or in batches. -- Role Hierarchy Management: Define which roles can administer other roles, enabling flexible permission delegation. - - -## Overview - -The AccessControlFacet provides a robust role-based access control (RBAC) system for your diamond. It allows granular permission management, enabling you to define roles, assign them to addresses, and enforce role-specific access to functions within the diamond. This facet is crucial for orchestrating secure operations and managing administrative privileges. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutFacet} from "@compose-protocol/diamond/contracts/facets/DiamondCutFacet.sol"; -import {DiamondLoupeFacet} from "@compose-protocol/diamond/contracts/facets/DiamondLoupeFacet.sol"; -import {AccessControlFacet} from "@compose-protocol/diamond/contracts/facets/AccessControlFacet.sol"; - -// Assume diamond contract and initialization are handled elsewhere - -contract AccessControlConsumer { - AccessControlFacet public acFacet; - - // Define role selectors (e.g., using interfaces or constants) - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - constructor(address _diamondAddress) { - // Get the AccessControlFacet from the diamond - acFacet = AccessControlFacet(_diamondAddress); - } - - function grantAdminRole(address _user) external { - // Caller must have the role that is the admin of ADMIN_ROLE (usually DEFAULT_ADMIN_ROLE) - acFacet.grantRole(ADMIN_ROLE, _user); - } - - function revokeOperatorRole(address _user) external { - // Caller must have the role that is the admin of OPERATOR_ROLE - acFacet.revokeRole(OPERATOR_ROLE, _user); - } - - function checkUserHasOperatorRole(address _user) external view returns (bool) { - return acFacet.hasRole(OPERATOR_ROLE, _user); - } - - function onlyOperator() external view { - // Reverts if caller does not have OPERATOR_ROLE - acFacet.requireRole(OPERATOR_ROLE); - // ... perform operator-only actions ... - } -}`} - - -## Best Practices - - -- Initialize roles and their admins during diamond deployment. Use `grantRole` or `grantRoleBatch` for initial assignments. -- Carefully manage role admin assignments. The role that administers another role determines who can grant or revoke the subordinate role. -- Use `requireRole` within your facet functions to enforce access control checks directly, ensuring that only authorized accounts can execute specific logic. - - -## Security Considerations - - -Access control checks are fundamental. Ensure that the caller has the appropriate role before executing sensitive operations. The `setRoleAdmin` function requires the caller to be the current admin of the role being modified. `grantRole` and `revokeRole` require the caller to be the admin of the role being granted or revoked. `renounceRole` allows a user to give up a role they hold, but the caller must be the account renouncing the role. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx deleted file mode 100644 index 30d711fe..00000000 --- a/website/docs/contracts/facets/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,388 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Contract documentation for AccessControlPausableFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for AccessControlPausableFacet - - - -- Role-specific pausing: Allows individual roles to be paused independently, providing fine-grained control. -- Admin-controlled operations: Only the designated administrator of a role can pause or unpause it, ensuring secure management. -- Emergency stop mechanism: Enables rapid disabling of role functionalities during critical events or maintenance. - - -## Overview - -The AccessControlPausableFacet provides granular control over role-based access by integrating pausing functionality. It allows specific roles to be temporarily disabled, preventing their associated operations while maintaining the underlying access control structure. This facet is crucial for managing emergency situations or performing maintenance without revoking permissions entirely. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose-protocol/diamond-proxy/facets/DiamondLoupeFacet.sol"; -import {AccessControlPausableFacet} from "@compose-protocol/diamond-proxy/facets/AccessControlPausableFacet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-proxy/interfaces/IDiamondCut.sol"; - -contract DeployDiamond { - function deploy() external { - // ... deployment logic for diamond proxy and initial facets ... - - AccessControlPausableFacet accessControlPausableFacet = new AccessControlPausableFacet(); - - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: address(accessControlPausableFacet), - action: IDiamondCut.FacetCutAction.ADD, - selectors: - DiamondLoupeFacet.getSelectors(accessControlPausableFacet) - }); - - // ... diamondCut call to add the AccessControlPausableFacet ... - } -} - -contract User { - AccessControlPausableFacet accessControlPausableFacet; - - function useAccessControlPausable(address diamondAddress) external { - accessControlPausableFacet = AccessControlPausableFacet(diamondAddress); - - // Example: Check if a role is paused - bool isPaused = accessControlPausableFacet.isRolePaused("someRole"); - - // Example: Pause a role (assuming caller is admin) - // accessControlPausableFacet.pauseRole("someRole"); - - // Example: Unpause a role (assuming caller is admin) - // accessControlPausableFacet.unpauseRole("someRole"); - } -}`} - - -## Best Practices - - -- Initialize roles and their administrators using the underlying AccessControl mechanism before attempting to pause or unpause them. -- Ensure that the caller has the necessary administrative privileges to pause or unpause a role, as enforced by the facet. -- Integrate `requireRoleNotPaused` checks within your application logic to prevent operations associated with paused roles. - - -## Security Considerations - - -This facet relies on the underlying AccessControl system for role management. Ensure that role administration is correctly configured to prevent unauthorized pausing or unpausing. The `requireRoleNotPaused` function is critical for preventing unintended operations when a role is paused. Reentrancy is not a direct concern for the pause/unpause functions themselves, but downstream logic called after a role is unpaused should be audited for reentrancy vulnerabilities. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx deleted file mode 100644 index 8433f8cb..00000000 --- a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,453 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Contract documentation for AccessControlTemporalFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for AccessControlTemporalFacet - - - -- Grants roles with a specific expiry timestamp. -- Provides a mechanism to check if a role assignment is currently valid and has not expired. -- Supports revoking temporal roles before their expiry. -- Enforces access control based on role validity and expiry. - - -## Overview - -The AccessControlTemporalFacet extends standard role-based access control by introducing time-bound role assignments. It allows granting roles with specific expiry timestamps and checking for their validity, enabling dynamic permission management within a Compose diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControl} from "@compose/diamond-contracts/contracts/interfaces/IAccessControl.sol"; -import {IAccessControlTemporal} from "@compose/diamond-contracts/contracts/interfaces/IAccessControlTemporal.sol"; - -// Assume diamond proxy and facet addresses are known -address constant DIAMOND_ADDRESS = address(0x123...); -address constant ACCESS_CONTROL_TEMPORAL_FACET_ADDRESS = address(0x456...); - -// Selector for grantRoleWithExpiry -bytes4 GRANT_ROLE_WITH_EXPIRY_SELECTOR = bytes4(keccak256("grantRoleWithExpiry(bytes32,address,uint64)")); - -// Selector for requireValidRole -bytes4 REQUIRE_VALID_ROLE_SELECTOR = bytes4(keccak256("requireValidRole(bytes32,address)")); - -// Example of granting a role with expiry -function grantTemporalRole(bytes32 _role, address _account, uint64 _expiryTimestamp) external { - (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(GRANT_ROLE_WITH_EXPIRY_SELECTOR, _role, _account, _expiryTimestamp)); - require(success, "Failed to grant temporal role"); -} - -// Example of checking for a valid role -function checkRoleValidity(bytes32 _role, address _account) external { - (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(REQUIRE_VALID_ROLE_SELECTOR, _role, _account)); - require(success, "Role is not valid or has expired"); -} -`} - - -## Best Practices - - -- Initialize roles and grant initial permissions using the `grantRoleWithExpiry` function during deployment or via an admin interface. -- Ensure the caller invoking `grantRoleWithExpiry` and `revokeTemporalRole` possesses the corresponding role's admin permission. -- Utilize `requireValidRole` before executing sensitive operations to enforce time-bound access controls. - - -## Security Considerations - - -Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role management. The `requireValidRole` function reverts if a role has expired or is not assigned, mitigating the risk of acting with stale permissions. Ensure that the `_expiryTimestamp` is set appropriately to prevent roles from remaining active indefinitely or expiring too soon. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx deleted file mode 100644 index e71251c1..00000000 --- a/website/docs/contracts/facets/DiamondCutFacet.mdx +++ /dev/null @@ -1,419 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Add=0, Replace=1, Remove=2" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Add=0, Replace=1, Remove=2 - - - -- Supports adding, replacing, and removing facets atomically. -- Allows for optional initialization of a new facet during an upgrade. -- Provides access to diamond storage layout and owner information. - - -## Overview - -The DiamondCutFacet provides the core functionality for managing facets within a Compose diamond. It enables adding new facets, replacing existing ones, and removing facets, acting as the primary interface for diamond upgrades and modifications. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - ---- -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutFacet} from "@compose-protocol/diamond-contracts/facets/DiamondCutFacet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/interfaces/IDiamondCut.sol"; - -contract DeployDiamondExample { - address internal diamondAddress; - - function deploy() external { - // Assume diamondAddress is initialized or deployed previously - diamondAddress = address(0xYourDiamondAddress); - - DiamondCutFacet diamondCutFacet = DiamondCutFacet(diamondAddress); - - // Example: Adding a new facet - bytes[] memory functionSelectors = new bytes[](1); - functionSelectors[0] = IDiamondCut.addFunctions.selector; - - address[] memory facetAddresses = new address[](1); - facetAddresses[0] = address(0xYourNewFacetAddress); - - diamondCutFacet.diamondCut( - IDiamondCut.FacetCut[] abi.encodePacked(IDiamondCut.FacetCut(facetAddresses[0], IDiamondCut.Action.Add, functionSelectors)), - address(0), // init contract - '[[\"initFunction\"]', // init calldata - diamondAddress // target diamond - ); - } -}`} - - -## Best Practices - - -- Initialize the diamond with the DiamondCutFacet as one of its initial facets to enable upgradeability from deployment. -- Use specific actions (Add, Replace, Remove) for clarity and to prevent unintended side effects during diamond upgrades. -- Store facet addresses and function selectors efficiently, considering gas costs for large numbers of facets. - - -## Security Considerations - - -Access control for `diamondCut` and related functions must be strictly enforced, typically restricted to the diamond owner or a designated admin role. Ensure that facet addresses and function selectors are validated to prevent the introduction of malicious code. Reentrancy is not a direct concern for the `diamondCut` function itself, but any `initCalldata` executed must be reentrancy-safe. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx deleted file mode 100644 index f7f9c37c..00000000 --- a/website/docs/contracts/facets/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,255 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "The functions in DiamondLoupeFacet MUST be added to a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -The functions in DiamondLoupeFacet MUST be added to a diamond. - - - -- Provides a standardized interface for querying diamond facet information. -- Enables detailed introspection of function selectors mapped to specific facet addresses. -- Optimizes memory usage for efficient retrieval of large numbers of facets and selectors. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows querying facet addresses, function selectors, and the overall facet structure of the diamond, enabling builders to understand and interact with the diamond's deployed logic. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondLoupeFacet} from "@compose/diamond-contracts/contracts/facets/DiamondLoupeFacet.sol"; - -contract DiamondLoupeConsumer { - IDiamondLoupeFacet constant DIAMOND_LOUPe = IDiamondLoupeFacet(0x1234567890abcdef1234567890abcdef1234567890); // Replace with actual diamond address - - function getFacetAddress(bytes4 _selector) external view returns (address) { - return DIAMOND_LOUPe.facetAddress(_selector); - } - - function getAllFacetAddresses() external view returns (address[] memory) { - return DIAMOND_LOUPe.facetAddresses(); - } - - function getFacetSelectors(address _facet) external view returns (bytes4[] memory) { - return DIAMOND_LOUPe.facetFunctionSelectors(_facet); - } - - function getAllFacets() external view returns (IDiamondLoupeFacet.Facet[] memory) { - return DIAMOND_LOUPe.facets(); - } -}`} - - -## Best Practices - - -- Integrate DiamondLoupeFacet into your diamond to enable discoverability and debugging of its functionalities. -- Use the provided functions to programmatically understand the diamond's architecture, especially during upgrades or when integrating new facets. -- Cache facet addresses locally if making frequent calls to the same facet to reduce gas costs. - - -## Security Considerations - - -DiamondLoupeFacet functions are read-only and do not modify state, posing minimal direct security risks. However, the information they provide can be used to understand the diamond's attack surface. Ensure that sensitive functions are correctly protected by access control mechanisms implemented in other facets. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx deleted file mode 100644 index 74bb89f5..00000000 --- a/website/docs/contracts/facets/ERC1155Facet.mdx +++ /dev/null @@ -1,679 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Supports both fungible and non-fungible tokens under a single interface. -- Provides batched operations for efficient transfers and balance checks. -- Includes URI resolution for token metadata, supporting both base and token-specific URIs. - - -## Overview - -The ERC1155Facet implements the ERC-1155 Multi-Token Standard interface. It provides functionality for managing fungible and non-fungible tokens within a Compose diamond, including token transfers, approvals, and batch operations. This facet enables a diamond to act as a comprehensive token issuer and manager. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Facet} from "@compose/diamond-contracts/facets/ERC1155/IERC1155Facet.sol"; - -contract ERC1155Consumer { - address immutable _diamondAddress; - - constructor(address diamondAddress) { - _diamondAddress = diamondAddress; - } - - function consumeERC1155() external { - IERC1155Facet erc1155Facet = IERC1155Facet(_diamondAddress); - - // Example: Check balance - uint256 balance = erc1155Facet.balanceOf(msg.sender, 1); - - // Example: Get URI - string memory uri = erc1155Facet.uri(1); - - // Example: Approve operator - erc1155Facet.setApprovalForAll(address(this), true); - } - - function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external pure override returns (bytes4) { - // Implement logic for receiving ERC1155 tokens - return - - } - - function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external pure override returns (bytes4) { - // Implement logic for receiving batched ERC1155 tokens - return - } -}`} - - -## Best Practices - - -- Ensure the `ERC1155Facet` is properly initialized with any necessary base URIs or token-specific URIs during diamond deployment. -- Implement the `onERC1155Received` and `onERC1155BatchReceived` callback functions in any contract that intends to receive ERC1155 tokens from the diamond to handle incoming transfers securely. - - -## Security Considerations - - -Input validation is crucial for token IDs and amounts to prevent unexpected behavior. Ensure that approvals granted via `setApprovalForAll` are managed carefully to avoid unintended token access. The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks to ensure the recipient contract implements the ERC1155TokenReceiver interface, mitigating reentrancy risks related to token reception. Access control for setting URIs or other administrative functions is not defined within this facet and must be managed by the diamond's access control mechanism. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx deleted file mode 100644 index ee79db2d..00000000 --- a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,420 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Enables permissioned cross-chain minting and burning of ERC20 tokens. -- Leverages the diamond storage pattern for accessing ERC20 and Access Control state. -- Enforces access control via the `trusted-bridge` role for sensitive cross-chain operations. - - -## Overview - -The ERC20BridgeableFacet manages cross-chain minting and burning operations for ERC20 tokens within a Compose diamond. It provides functions to interact with ERC20 and access control storage, enabling trusted bridge addresses to perform these sensitive operations. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose-protocol/diamond/contracts/interfaces/IDiamondCut.sol"; -import {AccessControlFacet} from "@compose-protocol/diamond/facets/AccessControl/AccessControlFacet.sol"; -import {ERC20BridgeableFacet} from "src/facets/ERC20BridgeableFacet.sol"; - -contract Deployer { - address diamondAddress; - - function deploy() public { - // ... Diamond deployment logic ... - // Assume diamondAddress is set after deployment - } - - function mintOnBridge(address _to, uint256 _amount) public { - ERC20BridgeableFacet erc20BridgeableFacet = ERC20BridgeableFacet(diamondAddress); - // Ensure the caller has the 'trusted-bridge' role before calling this externally - erc20BridgeableFacet.crosschainMint(_to, _amount); - } - - function burnOnBridge(address _from, uint256 _amount) public { - ERC20BridgeableFacet erc20BridgeableFacet = ERC20BridgeableFacet(diamondAddress); - // Ensure the caller has the 'trusted-bridge' role before calling this externally - erc20BridgeableFacet.crosschainBurn(_from, _amount); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is correctly assigned and managed within the AccessControlFacet before allowing external calls to `crosschainMint` and `crosschainBurn`. -- The `getERC20Storage` and `getAccessControlStorage` functions should only be called internally by other facets that require access to this specific storage. Direct external calls are not typical usage. -- Implement robust logging and monitoring for `crosschainMint` and `crosschainBurn` events to track cross-chain activity. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role, preventing unauthorized minting or burning. The `checkTokenBridge` internal function ensures that only addresses with the `trusted-bridge` role can initiate these operations, mitigating risks of unauthorized supply changes. Reentrancy is not a direct concern as these functions do not perform external calls to untrusted contracts; however, the underlying ERC20 token interaction should be considered. Input validation for `_to`, `_from`, and `_amount` is crucial to prevent underflow/overflow or minting/burning to invalid addresses. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx deleted file mode 100644 index eee4f796..00000000 --- a/website/docs/contracts/facets/ERC20BurnFacet.mdx +++ /dev/null @@ -1,260 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "Contract documentation for ERC20BurnFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ERC20BurnFacet - - - -- Allows burning of ERC20 tokens directly from the diamond, reducing external dependencies. -- Supports burning from the caller's balance (`burn`) and from another account's balance via allowance (`burnFrom`). -- Emits `Transfer` events to the zero address upon successful burns, aligning with ERC20 burn conventions. - - -## Overview - -The ERC20BurnFacet provides functionality to destroy ERC20 tokens within a Compose diamond. It allows users to burn their own tokens or burn tokens from another account if they have sufficient allowance, adhering to standard ERC20 burn semantics. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20BurnFacet} from "@compose/contracts/src/facets/ERC20BurnFacet.sol"; -import {DiamondProxy} from "@compose/contracts/src/DiamondProxy.sol"; - -contract Deployer { - function deploy() external { - // Assume diamondProxy is an already deployed DiamondProxy instance - DiamondProxy diamondProxy; - - // Add the ERC20BurnFacet to the diamond - address erc20BurnFacetAddress = address(new ERC20BurnFacet()); - bytes32[] memory selectors = new bytes32[](3); - selectors[0] = ERC20BurnFacet.getStorage.selector; - selectors[1] = ERC20BurnFacet.burn.selector; - selectors[2] = ERC20BurnFacet.burnFrom.selector; - - // The diamond proxy's loupe facet would typically handle facet additions - // For demonstration, we assume a direct call to an upgrade facet - // diamondProxy.diamondCut(...); // This is a placeholder for the actual diamond cut mechanism - - // Example: Burn 100 tokens from the caller's balance - // Assuming the diamondProxy is the target and the function is routed - (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(ERC20BurnFacet.burn.selector, 100)); - require(success, "Burn failed"); - - // Example: Burn 50 tokens from another address using allowance - // (bool successFrom, bytes memory dataFrom) = address(diamondProxy).call(abi.encodeWithSelector(ERC20BurnFacet.burnFrom.selector, spenderAddress, 50)); - // require(successFrom, "Burn from failed"); - } -}`} - - -## Best Practices - - -- Ensure the `ERC20BurnFacet` is correctly added to the diamond proxy during deployment or upgrades, mapping its functions to the appropriate selectors. -- When calling `burnFrom`, verify that the caller has an adequate allowance set for the tokens being burned. - - -## Security Considerations - - -This facet does not implement complex access control beyond standard ERC20 allowance checks for `burnFrom`. Ensure that the diamond's overall access control mechanisms are robust. Reentrancy is not a direct concern for `burn` and `burnFrom` as they do not make external calls. Input validation for token amounts should be handled by the diamond proxy's routing or by the caller. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx deleted file mode 100644 index 74c7ffe2..00000000 --- a/website/docs/contracts/facets/ERC20Facet.mdx +++ /dev/null @@ -1,576 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "Contract documentation for ERC20Facet" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ERC20Facet - - - -- Implements the full ERC20 standard interface for fungible tokens. -- Manages token balances, total supply, and allowances. -- Supports token transfers and approvals between accounts. -- Integrates seamlessly with the Compose diamond proxy architecture. - - -## Overview - -The ERC20Facet provides standard ERC20 token functionality within a Compose diamond. It manages token metadata, balances, allowances, and transfer operations, adhering to the ERC20 standard. This facet enables fungible token capabilities directly on the diamond proxy. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamond} from "@compose/diamond-proxy/src/diamond/IDiamond.sol"; - -contract ERC20Consumer { - IDiamond public diamondProxy; - - constructor(address _diamondProxyAddress) { - diamondProxy = IDiamond(_diamondProxyAddress); - } - - function getTokenName() external view returns (string memory) { - bytes32 selector = diamondProxy.getFunctionSelector("name()", "ERC20Facet"); - (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector)); - require(success, "ERC20Consumer: failed to get token name"); - return abi.decode(data, (string)); - } - - function getTokenBalance(address _account) external view returns (uint256) { - bytes32 selector = diamondProxy.getFunctionSelector("balanceOf(address)", "ERC20Facet"); - (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector, _account)); - require(success, "ERC20Consumer: failed to get token balance"); - return abi.decode(data, (uint256)); - } - - function transferTokens(address _to, uint256 _amount) external { - bytes32 selector = diamondProxy.getFunctionSelector("transfer(address,uint256)", "ERC20Facet"); - (bool success, ) = address(diamondProxy).call(abi.encodeWithSelector(selector, _to, _amount)); - require(success, "ERC20Consumer: failed to transfer tokens"); - } -}`} - - -## Best Practices - - -- Initialize the ERC20Facet with essential metadata (name, symbol, decimals) during diamond deployment or upgrade. -- Ensure appropriate access control is configured at the diamond level for sensitive operations like approvals and transfers if required by your application's design. -- Use the `getStorage` function to retrieve the ERC20 storage struct for direct manipulation if advanced or custom ERC20 logic is needed, adhering to Compose's storage pattern. - - -## Security Considerations - - -Ensure that the `approve` and `transferFrom` functions are used with caution to prevent unintended token spending. The `transfer` and `transferFrom` functions should validate that the sender has sufficient token balance before executing. Reentrancy is mitigated by standard ERC20 patterns and the diamond proxy's internal call handling. Input validation on addresses and amounts is crucial to prevent errors. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx deleted file mode 100644 index c3e25f59..00000000 --- a/website/docs/contracts/facets/ERC20PermitFacet.mdx +++ /dev/null @@ -1,350 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "Contract documentation for ERC20PermitFacet" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ERC20PermitFacet - - - -- Implements EIP-2612 `permit` functionality, allowing off-chain approvals. -- Utilizes EIP-712 for structured, secure signature encoding. -- Provides `nonces` and `DOMAIN_SEPARATOR` for signature generation utilities. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant on-chain signature-based approvals for ERC20 tokens. It allows token holders to grant allowances to spenders without needing to initiate a transaction themselves, enhancing user experience and gas efficiency. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - ---- -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20PermitFacet} from "@compose/diamond/facets/ERC20Permit/ERC20PermitFacet.sol"; -import {IDiamond} from "@compose/diamond/core/IDiamond.sol"; - -contract ERC20PermitConsumer { - address internal diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function consumePermit(address tokenAddress, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Assuming the ERC20PermitFacet is already deployed and attached to the diamond - // The selector for permit is keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)") - // This example assumes a direct call for simplicity, in a real scenario, - // you'd likely use the IDiamond interface or a helper function. - - // The actual call would be routed through the diamond proxy. - // For demonstration, we show the conceptual parameters. - - // address owner = ...; // The address that signed the permit - // uint256 nonce = ERC20PermitFacet(diamondAddress).nonces(owner); - // bytes32 domainSeparator = ERC20PermitFacet(diamondAddress).DOMAIN_SEPARATOR(); - - // The permit function itself handles the validation and allowance setting. - // The following is a simplified representation of the call structure. - - (bool success, ) = diamondAddress.call(abi.encodeWithSelector( - ERC20PermitFacet.permit.selector, - tokenAddress, // The address of the ERC20 token contract - spender, // The address to grant allowance to - amount, // The amount to allow - deadline, // The deadline for the permit - v, // Signature parameter v - r, // Signature parameter r - s // Signature parameter s - )); - - require(success, "Permit call failed"); - } -}`} - - -## Best Practices - - -- Ensure the `ERC20PermitFacet` is correctly initialized and attached to the diamond proxy before use. -- Use the `DOMAIN_SEPARATOR` and `nonces` functions to correctly construct EIP-712 compliant signatures for the `permit` function. -- Validate the `deadline` parameter to prevent stale permits from being used. - - -## Security Considerations - - -The `permit` function itself does not directly handle reentrancy as it primarily sets allowances. However, the underlying ERC20 token contract's `transferFrom` or similar functions, which will later consume this allowance, must be reentrancy-safe. Ensure the signature parameters (`v`, `r`, `s`) are validated correctly by the caller or a trusted off-chain service to prevent signature malleability or replay attacks. The `deadline` parameter is critical for preventing the use of expired permits. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx deleted file mode 100644 index 683e3642..00000000 --- a/website/docs/contracts/facets/ERC6909Facet.mdx +++ /dev/null @@ -1,530 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements core ERC-6909 functionalities including balance, allowance, transfer, and operator management. -- Provides explicit functions for setting and querying operator status, enabling delegated permissions. -- Leverages the diamond storage pattern for efficient and upgradeable state management. - - -## Overview - -The ERC6909Facet implements the ERC-6909 standard for fungible tokens, providing essential functions for managing token balances, allowances, and operator permissions within a Compose diamond. It serves as the primary interface for ERC-6909 token interactions, orchestrating internal state changes and access control. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909 } from "@compose/contracts/interfaces/IERC6909.sol"; -import { ERC6909Facet } from "@compose/contracts/facets/ERC6909Facet.sol"; - -contract ERC6909User { - address diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getUserBalance(uint256 _tokenId) public view returns (uint256) { - IERC6909 erc6909 = IERC6909(diamondAddress); - return erc6909.balanceOf(_tokenId); - } - - function transferTokens(uint256 _tokenId, address _to, uint256 _amount) public { - IERC6909 erc6909 = IERC6909(diamondAddress); - erc6909.transfer(_tokenId, _to, _amount); - } - - function approveSpender(uint256 _tokenId, address _spender, uint256 _amount) public { - IERC6909 erc6909 = IERC6909(diamondAddress); - erc6909.approve(_tokenId, _spender, _amount); - } -}`} - - -## Best Practices - - -- Initialize the ERC6909 storage correctly during diamond deployment using the appropriate initializer function. -- Ensure proper access control is configured for functions that modify token state (e.g., `transfer`, `transferFrom`, `approve`, `setOperator`), typically managed by a separate access control facet. -- When upgrading the ERC6909Facet, ensure that the storage layout remains compatible to prevent data loss or corruption. - - -## Security Considerations - - -Functions like `transfer` and `transferFrom` are susceptible to reentrancy if not protected. Ensure that the diamond's access control facet correctly restricts calls to authorized addresses. Input validation for token IDs, amounts, and addresses is crucial to prevent unexpected behavior or exploits. Ensure the `approve` function correctly handles allowance updates, especially when setting allowances to zero or increasing existing ones. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx deleted file mode 100644 index f2cb8bbc..00000000 --- a/website/docs/contracts/facets/ERC721BurnFacet.mdx +++ /dev/null @@ -1,205 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Provides an on-chain mechanism to permanently remove ERC721 tokens. -- Exposes a direct storage access function for advanced integration needs. - - -## Overview - -The ERC721BurnFacet provides the functionality to destroy ERC721 tokens within a Compose diamond. It exposes a `burn` function to remove tokens and a `getStorage` function for direct access to the underlying ERC721 storage. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "@compose/diamond-contracts/facets/ERC721/IERC721BurnFacet.sol"; - -contract ERC721BurnConsumer { - address immutable DIAMOND_ADDRESS; - - constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; - } - - function burnToken(uint256 tokenId) external { - IERC721BurnFacet(DIAMOND_ADDRESS).burn(tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721Storage` struct is correctly defined and initialized before deploying this facet. -- Accessing storage directly via `getStorage` requires careful handling to avoid slot collisions or data corruption. - - -## Security Considerations - - -The `burn` function should enforce ownership checks (e.g., only the token owner or an approved address can burn) to prevent unauthorized token destruction. The `getStorage` function bypasses standard access control and requires careful usage by developers to prevent state manipulation. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index a5687c01..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Efficiently burns ERC721 tokens by removing them from the internal enumeration tracking. -- Maintains the integrity of the token index and total supply after token destruction. -- Provides a dedicated function for token burning, separate from other ERC721 operations. - - -## Overview - -The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens while ensuring that the enumeration tracking is correctly updated. It allows for the removal of tokens from the diamond's state, maintaining the integrity of the enumerable list of tokens. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Enumerable, IERC721Burn} from "@compose/contracts/src/facets/ERC721/interfaces/IERC721Enumerable.sol"; -import {ERC721EnumerableFacet} from "@compose/contracts/src/facets/ERC721/ERC721EnumerableFacet.sol"; - -contract ExampleUsage { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 _tokenId) external { - IERC721Burn(diamondAddress).burn(_tokenId); - } - - function getFacetStorage() external view returns (ERC721EnumerableFacet.Layout memory) { - return ERC721EnumerableFacet.getStorage(diamondAddress); - } -}`} - - -## Best Practices - - -- Ensure the ERC721EnumerableBurnFacet is correctly registered with the Diamond Loupe facet for discoverability. -- When upgrading, ensure the storage layout compatibility is maintained to prevent data corruption. -- The `burn` function should only be called by authorized entities as defined by the diamond's access control. - - -## Security Considerations - - -The `burn` function should be protected by appropriate access control mechanisms to prevent unauthorized token destruction. Ensure that the `_tokenId` provided exists and is owned by the caller or an authorized entity before burning. Reentrancy is not a direct concern for this function, as it primarily modifies internal state and does not make external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx deleted file mode 100644 index e940ef04..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,764 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Full ERC721 compliance with added enumerable features (total supply, ownership indexing). -- Supports metadata retrieval via `tokenURI` for NFTs. -- Provides internal transfer logic (`internalTransferFrom`) for composability with other facets. -- Includes standard ERC721 approval mechanisms. - - -## Overview - -The ERC721EnumerableFacet provides the core functionality for an ERC721 Non-Fungible Token standard, including ownership tracking, approvals, and metadata retrieval. It extends ERC721 by adding enumerable properties like total supply and token ownership indexing, crucial for indexing and displaying token collections within a Compose diamond. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; -import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; - -contract DeployERC721Diamond { - address internal diamondCutAddress; - - function deploy() external { - // Assume diamondCutAddress is set or fetched - IDiamondCut diamondCut = IDiamondCut(diamondCutAddress); - - // Facet addresses would be deployed separately and referenced here - address erc721Facet = address(0x...) ; // Replace with actual deployed ERC721EnumerableFacet address - - // Define selectors for ERC721EnumerableFacet - bytes4[] memory selectors = new bytes4[](16); - selectors[0] = IERC721EnumerableFacet.getStorage.selector; - selectors[1] = IERC721EnumerableFacet.name.selector; - selectors[2] = IERC721EnumerableFacet.symbol.selector; - selectors[3] = IERC721EnumerableFacet.tokenURI.selector; - selectors[4] = IERC721EnumerableFacet.totalSupply.selector; - selectors[5] = IERC721EnumerableFacet.balanceOf.selector; - selectors[6] = IERC721EnumerableFacet.ownerOf.selector; - selectors[7] = IERC721EnumerableFacet.tokenOfOwnerByIndex.selector; - selectors[8] = IERC721EnumerableFacet.getApproved.selector; - selectors[9] = IERC721EnumerableFacet.isApprovedForAll.selector; - selectors[10] = IERC721EnumerableFacet.approve.selector; - selectors[11] = IERC721EnumerableFacet.setApprovalForAll.selector; - selectors[12] = IERC721EnumerableFacet.transferFrom.selector; - selectors[13] = IERC721EnumerableFacet.safeTransferFrom.selector; - selectors[14] = IERC721EnumerableFacet.safeTransferFrom.selector; // Overload for data - selectors[15] = IERC721EnumerableFacet.internalTransferFrom.selector; // Internal use - - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: erc721Facet, - action: IDiamondCut.FacetCutAction.ADD, - functionSelectors: selectors - }); - - // Execute the diamond cut to add the ERC721EnumerableFacet - // diamondCut.diamondCut(cut, address(0), ""); // Owner or authorized caller - } -} - -// Example of calling functions after deployment: -// IERC721EnumerableFacet nftContract = IERC721EnumerableFacet(diamondAddress); -// uint256 supply = nftContract.totalSupply(); -// address owner = nftContract.ownerOf(tokenId); -// nftContract.transferFrom(from, to, tokenId);`} - - -## Best Practices - - -- Initialize the facet with a name, symbol, and potentially a base URI during the diamond deployment process. -- Ensure access control for functions like `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` is handled by the diamond's access control facet. -- When upgrading or replacing this facet, ensure the storage layout remains compatible to avoid data loss or corruption, especially for token ownership mappings and enumerable lists. - - -## Security Considerations - - -Input validation for token IDs and addresses is critical and should be enforced by the facet's internal logic or an upstream access control mechanism. Reentrancy risks are mitigated by the standard ERC721 transfer patterns, but external calls (e.g., `tokenURI` resolvers or receiver contract callbacks in `safeTransferFrom`) must be carefully managed by the diamond's overall security design. The internal `transferFrom` function should only be callable by trusted internal facets or the diamond proxy itself to prevent unauthorized token movements. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx deleted file mode 100644 index c1233344..00000000 --- a/website/docs/contracts/facets/ERC721Facet.mdx +++ /dev/null @@ -1,668 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements the core ERC-721 standard for NFTs. -- Supports token metadata retrieval (name, symbol, tokenURI). -- Manages token ownership, balances, and approvals. -- Includes safe transfer mechanisms to prevent incompatible receiver issues. - - -## Overview - -The ERC721Facet provides a standard ERC-721 compliant interface for managing non-fungible tokens within a Compose diamond. It orchestrates token metadata, ownership, approvals, and transfers, serving as the primary surface area for ERC-721 interactions. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose/diamond/facets/ERC721Facet/IERC721Facet.sol"; -import {DiamondProxy} from "@compose/diamond/DiamondProxy.sol"; - -contract ERC721Consumer { - IERC721Facet public erc721Facet; - - constructor(address diamondProxyAddress) { - erc721Facet = IERC721Facet(diamondProxyAddress); - } - - function getTokenName() external view returns (string memory) { - return erc721Facet.name(); - } - - function getTokenSymbol() external view returns (string memory) { - return erc721Facet.symbol(); - } - - function getOwnerOfToken(uint256 tokenId) external view returns (address) { - return erc721Facet.ownerOf(tokenId); - } - - function transferToken(address from, address to, uint256 tokenId) external { - // Ensure caller has approval or is the owner - erc721Facet.transferFrom(from, to, tokenId); - } -}`} - - -## Best Practices - - -- Initialize the ERC721Facet with the correct storage slot via `getStorage()` for proper state management. -- Ensure proper access control for functions like `approve` and `setApprovalForAll` is handled by the diamond's access control facet. -- When upgrading, ensure that the storage layout remains compatible to prevent data corruption. - - -## Security Considerations - - -Access control for `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` should be enforced by the diamond's access control mechanism. The `internalTransferFrom` function performs critical ownership and approval checks; ensure it's called correctly by other facets or the diamond proxy. Reentrancy is mitigated by the diamond proxy pattern and the internal checks within `internalTransferFrom`. - - -
- -
- - diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx deleted file mode 100644 index 0272e287..00000000 --- a/website/docs/contracts/facets/ExampleDiamond.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Contract documentation for ExampleDiamond" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Contract documentation for ExampleDiamond - - - -- Acts as the central dispatcher for all external calls to the diamond. -- Manages the registration and unregistration of facets and their associated function selectors. -- Facilitates diamond initialization by setting the owner and adding initial facets. - - -## Overview - -The ExampleDiamond contract serves as the core of a Compose diamond. It acts as the central router, directing all external calls to the appropriate facet based on function selectors. This contract also manages diamond initialization, including setting the owner and registering facets. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ExampleDiamond} from "./ExampleDiamond.sol"; -import {DiamondCutFacet} from "@compose/diamond-core/facets/DiamondCutFacet.sol"; -import {DiamondLoupeFacet} from "@compose/diamond-core/facets/DiamondLoupeFacet.sol"; - -contract DeployExampleDiamond { - address public diamondAddress; - - function deploy() public { - // Example facet addresses and their selectors - address diamondCutFacet = address(new DiamondCutFacet()); - address diamondLoupeFacet = address(new DiamondLoupeFacet()); - - // Define facet cuts for initialization - ExampleDiamond.FacetCut[] memory facets = new ExampleDiamond.FacetCut[](2); - facets[0] = ExampleDiamond.FacetCut({ - facetAddress: diamondCutFacet, - action: ExampleDiamond.FacetCutAction.Add, - functionSelectors: DiamondCutFacet.getFunctionSelectors() - }); - facets[1] = ExampleDiamond.FacetCut({ - facetAddress: diamondLoupeFacet, - action: ExampleDiamond.FacetCutAction.Add, - functionSelectors: DiamondLoupeFacet.getFunctionSelectors() - }); - - // Deploy the diamond and initialize it - ExampleDiamond exampleDiamond = new ExampleDiamond(facets, msg.sender); - diamondAddress = address(exampleDiamond); - } -}`} - - -## Best Practices - - -- Initialize the diamond with essential facets like DiamondCutFacet and DiamondLoupeFacet during deployment. -- Ensure the owner is set correctly during initialization, as they will have the authority to manage facets. -- Use explicit `FacetCutAction` values (Add, Replace, Remove) for clarity and to prevent unintended state changes. - - -## Security Considerations - - -The constructor of ExampleDiamond sets the owner and registers facets. Ensure the owner address is trusted. Incorrectly configured facet cuts during initialization can lead to unexpected routing or loss of functionality. Access to `DiamondCutFacet` functions is typically restricted to the owner, preventing unauthorized modifications to the diamond's facet configuration. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx deleted file mode 100644 index 8cffb631..00000000 --- a/website/docs/contracts/facets/OwnerFacet.mdx +++ /dev/null @@ -1,210 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Provides clear ownership management for administrative control. -- Supports transferring ownership and renouncing it to a zero address. - - -## Overview - -The OwnerFacet manages ownership of the diamond proxy. It provides functions to retrieve the current owner, transfer ownership to a new address, and renounce ownership entirely. This facet is crucial for controlling administrative actions within the diamond. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; - -contract Deployer { - // Assume diamondAbi is the Application Binary Interface of the diamond proxy - // Assume diamondAddress is the address of the deployed diamond proxy - - IOwnerFacet ownerFacet = IOwnerFacet(diamondAddress); - - function getDiamondOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function transferDiamondOwnership(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function renounceDiamondOwnership() external { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize ownership during diamond deployment to a trusted address. -- Only the current owner should call `transferOwnership` or `renounceOwnership`. -- Store the `OwnerFacet`'s storage slot in a constant to ensure predictable access. - - -## Security Considerations - - -Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Ensure the `owner` address is managed securely to prevent unauthorized control of the diamond. Setting the owner to `address(0)` effectively removes ownership, so use `renounceOwnership` with caution. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx deleted file mode 100644 index ca9d7d4b..00000000 --- a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,291 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements a secure two-step ownership transfer mechanism. -- Allows for explicit acceptance of ownership, preventing accidental transfers. -- Provides functions to renounce ownership, returning the diamond to a state with no owner. - - -## Overview - -The OwnerTwoStepsFacet manages ownership of a diamond, implementing a two-step transfer process to enhance security. It provides functions to view the current and pending owner, initiate a transfer, and for the pending owner to accept or renounce the ownership. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - ---- -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "./interfaces/IOwnerTwoStepsFacet.sol"; - -contract OwnerTwoStepsExample { - IOwnerTwoStepsFacet ownerFacet; - - constructor(address _diamondAddress) { - ownerFacet = IOwnerTwoStepsFacet(_diamondAddress); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function transferDiamondOwnership(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function acceptDiamondOwnership() external { - ownerFacet.acceptOwnership(); - } - - function renounceDiamondOwnership() external { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize ownership transfers by calling `transferOwnership` with the desired new owner's address. -- The new owner must explicitly accept ownership by calling `acceptOwnership` to finalize the transfer. -- Store ownership-related data using `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` to ensure correct state management. - - -## Security Considerations - - -Access control for `transferOwnership`, `acceptOwnership`, and `renounceOwnership` is implicitly handled by the diamond's access control mechanism (e.g., EIP-2565). Ensure that only authorized entities can call these functions. The `owner()` and `pendingOwner()` functions are view functions and do not pose direct security risks. Direct access to storage pointers via `getOwnerStorage` and `getPendingOwnerStorage` should be done with extreme caution to avoid unintended state modifications. - - -
- -
- - diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx deleted file mode 100644 index f8b0a016..00000000 --- a/website/docs/contracts/facets/RoyaltyFacet.mdx +++ /dev/null @@ -1,196 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "**Title:**" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -**Title:** - - - -- Implements the ERC-2981 `royaltyInfo` function for standardized royalty queries. -- Supports token-specific royalty overrides on top of a default royalty configuration. -- Utilizes inline assembly for efficient access to its designated storage slot. - - -## Overview - -The RoyaltyFacet manages and exposes royalty information for NFTs within a Compose diamond. It implements the ERC-2981 standard, providing a standardized way to query royalty details for specific tokens and sale prices, ensuring creators receive their due compensation. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamond} from "@compose/diamond/contracts/IDiamond.sol"; -import {IRoyaltyFacet} from "@compose/diamond/facets/RoyaltyFacet/IRoyaltyFacet.sol"; - -contract RoyaltyConsumer { - IDiamond public diamond; - - constructor(address _diamondAddress) { - diamond = IDiamond(_diamondAddress); - } - - function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 feeBasisPoints) { - // Route to the RoyaltyFacet's royaltyInfo function - (bool success, bytes memory data) = diamond.diamondCut( - diamond.getFacetFunctionSelector(IRoyaltyFacet.royaltyInfo.selector), - abi.encodeCall(IRoyaltyFacet.royaltyInfo, (_tokenId, _salePrice)) - ); - require(success, "RoyaltyConsumer: Failed to get royalty info"); - (receiver, feeBasisPoints) = abi.decode(data, (address, uint256)); - return (receiver, feeBasisPoints); - } -}`} - - -## Best Practices - - -- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. -- Ensure the correct access control is in place if functions for setting default or token-specific royalties are implemented in other facets or a dedicated admin facet. -- When upgrading or adding facets, ensure the royalty storage slot (STORAGE_POSITION) remains consistent to avoid data loss. - - -## Security Considerations - - -The `royaltyInfo` function itself is read-only and poses no direct reentrancy risk. However, ensure that any facets responsible for *setting* royalty information (default or token-specific) implement robust access control and input validation to prevent unauthorized modifications. State coupling is minimal, as this facet primarily reads its own storage and does not interact with external state beyond what's necessary for its read operations. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx deleted file mode 100644 index 87153323..00000000 --- a/website/docs/contracts/modules/AccessControlMod.mdx +++ /dev/null @@ -1,446 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Role-based access control (RBAC) module for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control (RBAC) module for Compose diamonds - - - -- Role-Based Access Control (RBAC) for granular permission management. -- Functions to grant, revoke, and check for role ownership (`grantRole`, `revokeRole`, `hasRole`). -- Built-in `requireRole` function for easy and gas-efficient access control enforcement. -- Support for setting administrative roles for other roles to manage role hierarchies. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlMod provides a robust role-based access control (RBAC) system for Compose diamonds. It enables granular permission management by defining roles and assigning them to accounts, ensuring that only authorized entities can perform sensitive operations. This module is crucial for maintaining security and integrity within a diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlMod} from "@compose/modules/AccessControlMod.sol"; - -contract MyFacet { - IAccessControlMod internal accessControlMod; - - constructor(address _accessControlModAddress) { - accessControlMod = IAccessControlMod(_accessControlModAddress); - } - - function grantAdminRole(address _account) external { - bytes32 adminRole = accessControlMod.getRoleAdmin(IAccessControlMod.DEFAULT_ADMIN_ROLE()); - accessControlMod.grantRole(adminRole, _account); - } - - function onlyAdminCanDoSomething() external { - accessControlMod.requireRole(IAccessControlMod.DEFAULT_ADMIN_ROLE()); - // ... perform admin action ... - } -}`} - - -## Best Practices - - -- Use `requireRole` to enforce access control checks directly within your facet functions, reverting with a specific error if the caller lacks the necessary role. -- Grant and revoke roles using `grantRole` and `revokeRole` respectively, ensuring proper administrative oversight for role assignments. -- Be mindful of role hierarchy by using `setRoleAdmin` to define which roles can manage other roles, preventing unintended privilege escalation. - - -## Integration Notes - - -The AccessControlMod manages its state within its own storage, which is accessible via the diamond proxy. Facets interact with AccessControlMod through its interface. When calling functions like `grantRole`, `revokeRole`, or `requireRole`, the AccessControlMod's internal storage is updated or read. Ensure that the AccessControlMod facet is correctly initialized and accessible within the diamond's facet registry for proper operation. Changes to role assignments are immediately reflected and enforced by the AccessControlMod. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx deleted file mode 100644 index 6b385443..00000000 --- a/website/docs/contracts/modules/AccessControlPausableMod.mdx +++ /dev/null @@ -1,387 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Role-based access control with pause functionality for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control with pause functionality for Compose diamonds - - - -- Provides granular control over role pausing and unpausing, allowing temporary suspension of specific permissions. -- Integrates seamlessly with Compose's diamond proxy pattern for modularity and upgradeability. -- Enforces role-based access control with built-in checks for paused states, preventing unauthorized actions. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlPausable module provides robust role-based access control integrated with pause functionality for Compose diamonds. It ensures that sensitive operations can be temporarily halted for specific roles, enhancing security and operational control during critical periods. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausable} from "@compose/contracts/modules/accesscontrol/IAccessControlPausable.sol"; -import {AccessControlFacet} from "@compose/contracts/facets/accesscontrol/AccessControlFacet.sol"; - -contract MyFacet is IAccessControlPausable { - // Assume access to the diamond proxy and its facets - IAccessControlPausable internal accessControlPausable; - - constructor(address _diamondProxy) { - accessControlPausable = IAccessControlPausable(_diamondProxy); - } - - function grantRoleToUser(bytes32 _role, address _user) external { - // Example of using a core AccessControl function (assuming AccessControlFacet is also implemented) - AccessControlFacet(msg.sender).grantRole(_role, _user); - } - - function pauseAdminRole() external { - // Pause the 'ADMIN_ROLE' using the module function - accessControlPausable.pauseRole(AccessControlFacet.ADMIN_ROLE); - } - - function unpauseAdminRole() external { - // Unpause the 'ADMIN_ROLE' using the module function - accessControlPausable.unpauseRole(AccessControlFacet.ADMIN_ROLE); - } - - function performAdminAction() external { - // Ensure the caller has the ADMIN_ROLE and it's not paused before proceeding - accessControlPausable.requireRoleNotPaused(AccessControlFacet.ADMIN_ROLE, msg.sender); - // ... perform admin action ... - } -}`} - - -## Best Practices - - -- Always use `requireRoleNotPaused` before executing critical functions to ensure the caller has the necessary role and that the role is not currently paused. -- Implement custom errors for specific access control or pause-related failures to provide clearer revert reasons to users. -- When upgrading, ensure the `AccessControlPausableMod` facet is properly registered and its storage is compatible with previous versions. - - -## Integration Notes - - -This module relies on the diamond storage pattern. The `getAccessControlStorage` function returns storage related to the underlying access control mechanisms, while `getStorage` provides access to the specific state managed by `AccessControlPausable`. Facets interacting with this module should be aware of these storage layouts to correctly interpret and modify state. The ordering of storage variables within the `AccessControlPausable` struct is critical for compatibility during upgrades. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx deleted file mode 100644 index b4e436a0..00000000 --- a/website/docs/contracts/modules/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,480 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Time-limited role-based access control for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Time-limited role-based access control for Compose diamonds - - - -- Grants roles with a specific expiry timestamp. -- Checks for both role assignment and non-expiry status. -- Provides granular control over temporary access permissions. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides time-limited role-based access control for Compose diamonds. It allows granting roles that automatically expire after a specified timestamp, enhancing security and manageability by enforcing temporary permissions. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; -import {AccessControlUnauthorizedAccount, AccessControlRoleExpired} from "@compose/modules/access-control-temporal/Errors.sol"; - -contract MyFacet { - IAccessControlTemporalMod internal accessControlTemporalMod; - - // Assume accessControlTemporalMod is initialized elsewhere, e.g., in the diamond deployer - - function _grantAdminRoleTemporary(address _user, uint64 _expiry) internal { - accessControlTemporalMod.grantRoleWithExpiry(keccak256("ADMIN_ROLE"), _user, _expiry); - } - - function _requireAdmin(address _user) internal { - try accessControlTemporalMod.requireValidRole(keccak256("ADMIN_ROLE"), _user) { - // Role is valid, proceed - } catch AccessControlUnauthorizedAccount { - revert("User does not have the ADMIN_ROLE"); - } catch AccessControlRoleExpired { - revert("ADMIN_ROLE has expired for this user"); - } - } -}`} - - -## Best Practices - - -- Use `requireValidRole` to enforce both role existence and non-expiry before critical operations. -- Handle `AccessControlUnauthorizedAccount` and `AccessControlRoleExpired` errors explicitly in your facets to provide clear user feedback. -- Carefully manage expiry timestamps to ensure roles are revoked in a timely manner, preventing stale permissions. - - -## Integration Notes - - -This module interacts with Compose's diamond storage pattern. Facets can access its storage and functions via the `IAccessControlTemporalMod` interface. The `grantRoleWithExpiry` and `revokeTemporalRole` functions directly modify the temporal role assignments within the diamond's state. The `requireValidRole` function reads this state to enforce access control checks, reverting if the role is unassigned or expired. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx deleted file mode 100644 index e87bee2f..00000000 --- a/website/docs/contracts/modules/DiamondCutMod.mdx +++ /dev/null @@ -1,345 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Diamond upgrade (cut) module for ERC-2535 diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond upgrade (cut) module for ERC-2535 diamonds - - - -- All functions are `internal` for use in custom facets -- Follows diamond storage pattern (EIP-8042) -- Compatible with ERC-2535 diamonds -- No external dependencies or `using` directives - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -Diamond upgrade (cut) module for ERC-2535 diamonds - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Integration Notes - - -This module accesses shared diamond storage, so changes made through this module are immediately visible to facets using the same storage pattern. All functions are internal as per Compose conventions. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx deleted file mode 100644 index 82201456..00000000 --- a/website/docs/contracts/modules/DiamondMod.mdx +++ /dev/null @@ -1,241 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Diamond Library - Internal functions and storage for diamond proxy functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond Library - Internal functions and storage for diamond proxy functionality. - - - -- Facet Management: Enables adding facets and their associated function selectors to the diamond during deployment. -- Function Dispatch: Provides the fallback mechanism to route external calls to the correct facet implementation. -- Internal Storage Access: Offers a way to inspect diamond storage slots internally. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -DiamondMod provides core internal functions for managing diamond proxy facets and handling function calls. It is essential for the diamond's runtime logic, enabling facet registration and dispatch, crucial for composability and upgradeability within the Compose framework. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondMod} from "@compose/diamond/contracts/diamond/IDiamondMod.sol"; - -contract ExampleFacet { - IDiamondMod internal diamondMod; - - // Assume diamondMod is initialized elsewhere, e.g., during deployment - constructor(address _diamondMod) { - diamondMod = IDiamondMod(_diamondMod); - } - - /** - * @notice Example of interacting with diamondMod to get storage. - * @dev This function is for demonstration purposes only. - */ - function exampleGetStorage() external view returns (bytes32 storageValue) { - // Example: retrieve a specific storage slot. Replace '0x...' with the actual slot. - // Note: This is a simplified example; direct storage access should be done with caution. - storageValue = diamondMod.getStorage(address(this), bytes32(uint256(0))); // Placeholder slot - return storageValue; - } -}`} - - -## Best Practices - - -- Use `addFacets` only during initial diamond deployment to prevent runtime modification of the diamond's function-to-facet mappings. -- Ensure `diamondFallback` is correctly implemented and called by the diamond proxy to route calls to the appropriate facets. -- Access storage via `getStorage` with caution, understanding that storage layout and slot allocation are critical for diamond integrity. - - -## Integration Notes - - -DiamondMod interacts directly with the diamond proxy's storage. The `addFacets` function is intended for initial deployment only and modifies the internal mapping of function selectors to facet addresses. `diamondFallback` acts as the central dispatcher, looking up the facet for a given function selector and executing it. `getStorage` allows internal access to the diamond's storage slots. Changes made via `addFacets` are persistent and define the diamond's runtime behavior. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx deleted file mode 100644 index 2f621add..00000000 --- a/website/docs/contracts/modules/ERC1155Mod.mdx +++ /dev/null @@ -1,618 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens. - - - -- Supports both single token and batch operations for minting, burning, and transfers, optimizing gas usage for multiple operations. -- Implements receiver validation for contract addresses during minting and transfers, ensuring adherence to the ERC1155 safe transfer mechanism. -- Allows setting a base URI and token-specific URIs, enabling rich metadata representation for ERC1155 tokens. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC1155Mod provides a robust implementation of the ERC-1155 standard, enabling the management of fungible and non-fungible tokens within a Compose diamond. It facilitates token minting, burning, and transfers while adhering to safety checks for contract recipients, crucial for composability and secure asset handling. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import {ERC1155Mod} from "./ERC1155Mod.sol"; // Assuming ERC1155Mod is deployed as a facet - -contract MyFacet is IERC1155Receiver { - address diamondProxy; // Address of the Compose diamond - - constructor(address _diamondProxy) { - diamondProxy = _diamondProxy; - } - - function mintNewTokens(address _to, uint256 _id, uint256 _amount) external { - // Call the mint function from the ERC1155Mod facet - ERC1155Mod(diamondProxy).mint(_to, _id, _amount); - } - - function transferMyTokens(address _from, address _to, uint256 _id, uint256 _amount) external { - // Call the safeTransferFrom function from the ERC1155Mod facet - ERC1155Mod(diamondProxy).safeTransferFrom(_from, _to, _id, _amount, ""); - } - - // Implement IERC1155Receiver functions for contract interaction - function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) { - // Handle received ERC1155 tokens - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) { - // Handle received batch of ERC1155 tokens - return this.onERC1155BatchReceived.selector; - } -}`} - - -## Best Practices - - -- Always validate the `_to` address in `mint` and `mintBatch` functions when it is a contract to ensure it implements `IERC1155Receiver` for safe transfers. -- Use `safeTransferFrom` and `safeBatchTransferFrom` for all external transfers to ensure receiver contract adherence to the ERC1155 standard. -- When overriding or extending functionality, ensure calls to `burn`, `burnBatch`, `mint`, and `mintBatch` correctly update balances and emit the appropriate `TransferSingle` or `TransferBatch` events. - - -## Integration Notes - - -The ERC1155Mod interacts with a predefined diamond storage slot for its state, including token balances, approvals, and URI configurations. Facets can access this storage via the `getStorage` function. Any changes made to the storage by the ERC1155Mod functions (e.g., balance updates, URI settings) are directly reflected in the diamond's state and are visible to all facets. The order of storage variables within the ERC1155 storage struct must be maintained when composing with other modules to avoid state corruption. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx deleted file mode 100644 index 10255b35..00000000 --- a/website/docs/contracts/modules/ERC165Mod.mdx +++ /dev/null @@ -1,158 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. - - - -- Implements the ERC-165 standard for interface detection. -- Provides a dedicated storage slot for interface support tracking. -- Enables facets to programmatically declare supported interfaces. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod provides the necessary logic and storage for implementing the ERC-165 standard within a Compose diamond. This enables contracts to programmatically report their supported interfaces, enhancing interoperability and discoverability for diamond facets. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC165Mod} from "@compose/diamond/modules/ERC165/IERC165Mod.sol"; - -contract MyERC721Facet { - struct Storage { - // ... other storage variables - IERC165Mod.ERC165Storage erc165Storage; - } - - function initialize(Storage storage self, address _diamondAddress) external { - // Register ERC721 interface during facet initialization - IERC165Mod.registerInterface(self.erc165Storage, type(IERC721).interfaceId); - } - - function supportsInterface(bytes4 interfaceId) external view returns (bool) { - // Delegate to the ERC165Mod logic - return IERC165Mod.supportsInterface(erc165Storage, interfaceId); - } -}`} - - -## Best Practices - - -- Ensure `registerInterface` is called during facet initialization to correctly register supported interfaces. -- Access the ERC165 storage via `getStorage` to ensure correct slot binding for interface detection. - - -## Integration Notes - - -The ERC165Mod utilizes a dedicated storage struct, `ERC165Storage`, which must be included in the facet's storage layout. The `getStorage` function, using inline assembly, binds this struct to its designated storage slot, ensuring that `supportsInterface` queries correctly retrieve interface data. It is crucial to call `registerInterface` during facet initialization to populate this storage with the interfaces the facet supports. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx deleted file mode 100644 index f0d6efcd..00000000 --- a/website/docs/contracts/modules/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,439 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "LibERC20Bridgeable — ERC-7802 Library" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Bridgeable — ERC-7802 Library - - - -- Enforces `trusted-bridge` role for cross-chain minting and burning operations, enhancing security. -- Provides helper functions (`getERC20Storage`, `getAccessControlStorage`) for accessing module-specific storage via diamond slots. -- Designed for integration into Compose diamonds, leveraging the diamond storage pattern for modularity. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20BridgeableMod module provides functionality for cross-chain ERC20 token operations. It enables secure minting and burning of tokens across different blockchains by enforcing access control for trusted bridge operators. This module is crucial for maintaining the integrity and security of cross-chain token transfers within a Compose diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableMod} from "./interfaces/IERC20BridgeableMod.sol"; - -contract MyERC20Facet { - address constant ERC20_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.erc20")))); - address constant ACCESS_CONTROL_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.accessControl")))); - - IERC20BridgeableMod private erc20BridgeableMod; - - function initialize(address _diamondAddress) external { - erc20BridgeableMod = IERC20BridgeableMod(_diamondAddress); - } - - /** - * @notice Burns ERC20 tokens for a cross-chain operation. - * @param _token The address of the ERC20 token. - * @param _from The address from which tokens are burned. - * @param _amount The amount of tokens to burn. - */ - function burnForBridge(address _token, address _from, uint256 _amount) external { - // Assume caller is a trusted bridge operator, enforced by the module. - erc20BridgeableMod.crosschainBurn(_token, _from, _amount); - } - - /** - * @notice Mints ERC20 tokens for a cross-chain operation. - * @param _token The address of the ERC20 token. - * @param _to The address to which tokens are minted. - * @param _amount The amount of tokens to mint. - */ - function mintFromBridge(address _token, address _to, uint256 _amount) external { - // Assume caller is a trusted bridge operator, enforced by the module. - erc20BridgeableMod.crosschainMint(_token, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure that only authorized addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint` functions. The module enforces this, but proper role management is essential. -- When integrating, be mindful of the storage slot assignments for ERC20 and AccessControl data, as specified by the module. Deviating can lead to incorrect behavior. -- Handle potential reverts from `checkTokenBridge` gracefully in calling facets, although the module's internal checks aim to prevent this by design. - - -## Integration Notes - - -This module interacts with the diamond's storage to manage ERC20 token states and access control roles. The `getERC20Storage` and `getAccessControlStorage` functions retrieve references to these storage areas using predefined diamond storage slots. Facets integrating with this module will call its functions, which in turn perform checks against the AccessControl storage to verify the caller's `trusted-bridge` role before executing mint or burn operations. Ensure that the diamond's storage layout is compatible with the slots expected by this module. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx deleted file mode 100644 index c911ed4b..00000000 --- a/website/docs/contracts/modules/ERC20Mod.mdx +++ /dev/null @@ -1,429 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic. - - - -- Provides `mint`, `burn`, `transfer`, and `transferFrom` internal functions for standard ERC-20 operations. -- Manages ERC-20 token balances and total supply. -- Includes an `approve` function to manage spender allowances. -- Utilizes `getStorage` to provide a reference to the ERC-20 specific storage slot. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod module provides essential internal functions and storage for ERC-20 token logic within a Compose diamond. It enables standard ERC-20 operations like minting, burning, transferring, and approving allowances, ensuring composability and adherence to the ERC-20 standard. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod } from "./interfaces/IERC20Mod.sol"; -import { ERC20Storage } from "./storage/ERC20Storage.sol"; - -contract ERC20Facet { - // Assume ERC20Storage is correctly laid out in the diamond's storage - // and IERC20Mod is the interface for the ERC20Mod library. - - function mintTokens(address _to, uint256 _amount) external { - // Obtain a pointer to the ERC20 storage struct - ERC20Storage storage erc20 = IERC20Mod.getStorage(); - - // Call the internal mint function from the ERC20Mod library - IERC20Mod.mint(erc20, address(this), _to, _amount); - } - - function transferTokens(address _from, address _to, uint256 _amount) external { - ERC20Storage storage erc20 = IERC20Mod.getStorage(); - IERC20Mod.transfer(erc20, _from, _to, _amount); - } - - function approveAllowance(address _spender, uint256 _amount) external { - ERC20Storage storage erc20 = IERC20Mod.getStorage(); - IERC20Mod.approve(erc20, msg.sender, _spender, _amount); - } -}`} - - -## Best Practices - - -- Access control for minting and burning should be enforced by the calling facet, as ERC20Mod provides only the core logic. -- Ensure the `ERC20Storage` struct is correctly initialized and laid out in the diamond's storage to prevent state corruption. -- Handle potential `ERC20TransferFailed` or other custom errors gracefully in your facet logic. - - -## Integration Notes - - -The ERC20Mod relies on a specific storage slot for its `ERC20Storage` struct. Facets using this module must ensure that the diamond's storage layout correctly allocates this slot and that the `ERC20Storage` struct definition (including variable order and types) is identical to the one used internally by ERC20Mod. The `getStorage` function uses inline assembly to reliably access this storage position. Changes to the `ERC20Storage` struct definition or its storage slot will break compatibility and require diamond upgrades. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx deleted file mode 100644 index 15d23eb0..00000000 --- a/website/docs/contracts/modules/ERC20PermitMod.mdx +++ /dev/null @@ -1,309 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage" -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage - - - -- Implements ERC-2612 permit logic, enabling gas-efficient off-chain token approvals. -- Provides a self-contained domain separator calculation, preventing signature replay attacks across different contracts or chains. -- Designed for integration into Compose diamonds, leveraging the diamond storage pattern for state management. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20PermitMod library provides self-contained logic and storage for implementing ERC-2612 permit functionality within a Compose diamond. This enables gas-efficient, off-chain approvals for ERC-20 token transfers, enhancing composability and user experience by reducing on-chain transaction costs for approvals. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20PermitMod, ERC20PermitMod} from "@compose-protocol/contracts/src/modules/ERC20PermitMod.sol"; -import {DiamondStorage} from "@compose-protocol/contracts/src/DiamondStorage.sol"; - -contract MyERC20Facet { - using ERC20PermitMod for ERC20PermitMod.PermitStorage; - - DiamondStorage internal diamondStorage; - - /** - * @notice Approves a spender to withdraw tokens on behalf of the owner using a permit signature. - * @dev This function must be implemented by the facet calling the ERC20PermitMod library. - * @param _owner The address of the token owner. - * @param _spender The address to be approved. - * @param _value The amount of tokens to be approved. - * @param _deadline The deadline for the permit. - * @param _v The v component of the signature. - * @param _r The r component of the signature. - * @param _s The s component of the signature. - */ - function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { - // Ensure the facet has access to the diamond storage. - // The actual storage access will depend on the Compose diamond implementation. - ERC20PermitMod.PermitStorage storage permitStorage = _getPermitStorage(); - - // Call the permit logic from the library. - permitStorage.permit(_owner, _spender, _value, _deadline, _v, _r, _s); - } - - /** - * @notice Retrieves the permit storage for the ERC20PermitMod. - * @return permitStorage The storage struct for permit logic. - */ - function _getPermitStorage() internal view returns (ERC20PermitMod.PermitStorage storage) { - // This is a placeholder. Actual storage retrieval depends on the diamond's storage layout. - // It would typically involve accessing a specific slot in the diamond storage. - return ERC20PermitMod.getPermitStorage(diamondStorage); - } - - /** - * @notice Returns the domain separator used in the encoding of the signature for {permit}. - * @return The domain separator. - */ - function DOMAIN_SEPARATOR() external view returns (bytes32) { - ERC20PermitMod.PermitStorage storage permitStorage = _getPermitStorage(); - return permitStorage.DOMAIN_SEPARATOR(); - } -} -`} - - -## Best Practices - - -- Ensure the `permit` function in your facet correctly emits the `Approval` event after calling the library function, as the library itself does not emit events. -- Validate the `_deadline` parameter to prevent expired permits from being used and consider using a sufficiently distant deadline to accommodate off-chain signing and on-chain execution delays. -- Carefully manage access control for the `permit` function to ensure only authorized entities can execute permit approvals. - - -## Integration Notes - - -The ERC20PermitMod library manages its own state through a `PermitStorage` struct. Facets integrating this module must correctly retrieve and pass this storage slot to the library functions. The `getPermitStorage` function within the library is responsible for accessing this state, which is assumed to be managed within the diamond's overall storage layout. Facets are responsible for calling the `permit` function and ensuring the associated `Approval` event is emitted externally. The domain separator is calculated based on the contract's address and chain ID, ensuring uniqueness for replay protection. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx deleted file mode 100644 index 1cbd2017..00000000 --- a/website/docs/contracts/modules/ERC6909Mod.mdx +++ /dev/null @@ -1,524 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic. - - - -- Implements the core logic for ERC-6909 minimal multi-token standard. -- Utilizes diamond storage via inline assembly for efficient state access. -- Supports standard token operations: mint, burn, transfer, and approve. -- Includes operator functionality for delegated transfers. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC6909Mod provides the core internal logic and storage for implementing the ERC-6909 minimal multi-token standard within a Compose diamond. It enables facets to manage token approvals, transfers, minting, and burning efficiently by leveraging the diamond's storage pattern. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Mod, IERC6909ModStorage} from "@compose-protocol/diamond-contracts/contracts/modules/ERC6909/ERC6909Mod.sol"; -import {DiamondStorage} from "@compose-protocol/diamond-contracts/contracts/core/DiamondStorage.sol"; - -contract ERC6909Facet { - using DiamondStorage for IDiamondStorage; - - function mintToken(uint256 _id, uint256 _amount, address _to) external { - address contractAddress = address(this); - contractAddress.call(abi.encodeWithSignature(\"mint(uint256,uint256,address)\", _id, _amount, _to)); - } - - function transferToken(uint256 _id, uint256 _amount, address _from, address _to) external { - address contractAddress = address(this); - contractAddress.call(abi.encodeWithSignature(\"transfer(uint256,uint256,address,address)\", _id, _amount, _from, _to)); - } - - function approveToken(uint256 _id, address _spender, uint256 _amount) external { - address contractAddress = address(this); - contractAddress.call(abi.encodeWithSignature(\"approve(uint256,address,uint256)\", _id, _spender, _amount)); - } -}`} - - -## Best Practices - - -- Ensure that access control for mint, burn, and setOperator functions is implemented at the facet level, as the module itself is permissionless. -- When interacting with the module, always use the diamond's proxy address for function calls to ensure state is managed correctly within the diamond's storage. -- Be mindful of allowance deduction logic in `transfer`. Special handling for `type(uint256).max` and operator status is crucial for correct behavior. - - -## Integration Notes - - -The ERC6909Mod relies on a specific storage slot defined by `STORAGE_POSITION` within the diamond's `DiamondStorage` struct. Facets interact with this module by calling its functions through the diamond proxy. The module directly reads and writes to the `ERC6909ModStorage` struct, which is part of the overall diamond storage. Ensure that no other facets or modules attempt to use the same storage slot to prevent state corruption. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx deleted file mode 100644 index fe817335..00000000 --- a/website/docs/contracts/modules/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,372 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. - - - -- Manages internal token enumeration lists for ERC-721 tokens. -- Integrates seamlessly with minting, burning, and transfer operations to maintain list integrity. -- Utilizes inline assembly for efficient retrieval of its storage slot from the diamond's storage. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721EnumerableMod provides the core logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures tokens are correctly added to and removed from enumeration lists during minting, burning, and transfers, maintaining the integrity of token ownership and ordering for on-chain querying. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod} from "./IERC721EnumerableMod.sol"; -import {ERC721Storage} from "./ERC721Storage.sol"; - -contract MyERC721Facet { - // Assume diamond storage is accessible and initialized. - // For example purposes, we'll simulate it here. - ERC721Storage internal _erc721Storage; - - // Assume this facet has access to the diamond proxy's storage layout - // and specifically the storage slot for ERC721EnumerableMod. - // The actual implementation would involve fetching this from the diamond's storage. - function _getEnumerableMod() internal view returns (IERC721EnumerableMod) { - // This is a placeholder. In a real diamond, this would fetch the - // contract address of the ERC721EnumerableMod facet. - return IERC721EnumerableMod(address(this)); // Replace with actual fetch - } - - /** - * @notice Mints a new ERC-721 token. - * @param _to The address to mint the token to. - * @param _tokenId The ID of the token to mint. - */ - function mintToken(address _to, uint256 _tokenId) external { - require(_to != address(0), "ERC721: mint to the zero address"); - // Assume ownership and approval checks are handled by a separate facet or logic. - _getEnumerableMod().mint(_to, _tokenId); - } - - /** - * @notice Transfers an ERC-721 token. - * @param _from The address to transfer the token from. - * @param _to The address to transfer the token to. - * @param _tokenId The ID of the token to transfer. - */ - function transferToken(address _from, address _to, uint256 _tokenId) external { - require(_to != address(0), "ERC721: transfer to the zero address"); - // Assume ownership and approval checks are handled by a separate facet or logic. - _getEnumerableMod().transferFrom(_from, _to, _tokenId); - } - - /** - * @notice Burns an ERC-721 token. - * @param _tokenId The ID of the token to burn. - */ - function burnToken(uint256 _tokenId) external { - // Assume ownership and approval checks are handled by a separate facet or logic. - _getEnumerableMod().burn(_tokenId); - } -} -`} - - -## Best Practices - - -- Ensure that access control for `mint`, `burn`, and `transferFrom` is enforced by the calling facet or an upstream authorization mechanism, as the module itself primarily handles state updates for enumeration. -- When upgrading facets that interact with `ERC721EnumerableMod`, verify that the storage layout remains compatible to prevent data corruption or loss. -- Handle potential reverts from the module's functions gracefully by implementing appropriate error handling in the calling facet. - - -## Integration Notes - - -The ERC721EnumerableMod interacts directly with the diamond's storage, specifically managing state within its own predefined storage slot. Facets that use this module will call its functions, and the module will directly modify the diamond's storage. The `getStorage` function demonstrates how to access this specific storage slot using inline assembly. It is crucial for facets to correctly identify and interact with this storage slot to ensure proper functioning. Any changes to the module's storage layout in future upgrades must be carefully managed to maintain backward compatibility for existing tokens and facets. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx deleted file mode 100644 index 4b958d74..00000000 --- a/website/docs/contracts/modules/ERC721Mod.mdx +++ /dev/null @@ -1,365 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. - - - -- Provides internal, reusable logic for core ERC-721 operations. -- Interacts directly with diamond storage for state management. -- Enables composability by abstracting complex ERC-721 state transitions. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod module provides essential internal logic for managing ERC-721 compliant tokens within a Compose diamond. It enables facets to securely mint, burn, and transfer tokens by interacting directly with the diamond's storage, ensuring state consistency and composability. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod, IERC721Storage} from "./interfaces/IERC721Mod.sol"; -import {ERC721Mod} from "./ERC721Mod.sol"; - -contract MyERC721Facet { - address constant ERC721_MOD_SLOT = 1; // Example storage slot - - function mintToken(address _to, uint256 _tokenId) external { - ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); - erc721Mod.mint(_to, _tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); - erc721Mod.transferFrom(_from, _to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); - erc721Mod.burn(_tokenId); - } - - function getERC721Storage() internal view returns (IERC721Storage.ERC721Storage memory) { - ERC721Mod erc721Mod = ERC721Mod(ERC721_MOD_SLOT); - return erc721Mod.getStorage(); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721Mod` is deployed to a consistent, well-known storage slot across all facets that interact with it. -- Perform necessary access control checks within your facet before calling `ERC721Mod` functions like `mint` or `transferFrom`. -- Handle potential reverts from `ERC721Mod` functions gracefully, especially for non-existent tokens during `burn` or `transferFrom`. - - -## Integration Notes - - -The `ERC721Mod` module is designed to be called by custom facets. It accesses and modifies the diamond's storage directly via a predefined storage slot (specified by the `getStorage` function's implementation, though this is an internal detail). Facets using this module must ensure that the storage slot for `ERC721Mod` is correctly initialized and accessible. The `getStorage` function returns the ERC721 storage struct, allowing facets to read current ERC-721 state. - - -
- -
- - diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx deleted file mode 100644 index d35fbcbc..00000000 --- a/website/docs/contracts/modules/NonReentrancyMod.mdx +++ /dev/null @@ -1,142 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. - - - -- Prevents reentrant function calls by maintaining a state flag. -- Offers `enter` and `exit` functions for explicit control over reentrancy guards. -- Designed for integration into any facet requiring protection against recursive execution. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod provides essential mechanisms to prevent reentrant calls within your diamond facets. By implementing `enter` and `exit` functions, you can safeguard critical operations from recursive execution, ensuring contract stability and predictable state transitions. - ---- - -## Storage - ---- -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose/core/src/libraries/LibNonReentrancy.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 internal _nonReentrancyState; - - /** - * @notice Performs a sensitive operation without allowing reentrancy. - */ - function sensitiveOperation() external { - // Lock reentrancy before executing sensitive logic. - _nonReentrancyState.enter(); - - try { - // ... sensitive logic here ... - } finally { - // Ensure reentrancy is always re-enabled, even if an error occurs. - _nonReentrancyState.exit(); - } - } -}`} - - -## Best Practices - - -- Always pair `enter()` with `exit()` to prevent reentrancy. Use `try/finally` blocks to guarantee `exit()` is called, even on reverts. -- Use `enter()` at the very beginning of a function and `exit()` at the very end to ensure the entire function body is protected. -- Consider the state variable used to track reentrancy status; it should be unique per guarded function or operation. - - -## Integration Notes - - -The `LibNonReentrancy` library requires a state variable within your facet to track the reentrancy status. This variable is typically a `uint256` or similar type, where a value of `1` signifies that the function is currently executing (reentrancy locked) and `0` signifies it is available. Facets using this library should initialize this state variable to `0` and call `enter()` before executing protected logic and `exit()` immediately after. Changes to this state variable are local to the facet and do not directly interact with diamond storage beyond the facet's own storage slot. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx deleted file mode 100644 index caa8b2d7..00000000 --- a/website/docs/contracts/modules/OwnerMod.mdx +++ /dev/null @@ -1,250 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. - - - -- Implements ERC-173 ownership standard for clear contract accountability. -- Provides `requireOwner()` for straightforward access control based on contract ownership. -- Supports explicit ownership transfer via `transferOwnership()`, including ownership renouncement. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod module provides essential functionality for managing contract ownership according to the ERC-173 standard. It enables secure owner retrieval, owner-only access control, and ownership transfer, crucial for administrative operations within a Compose diamond. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; - -contract MyOwnerFacet { - address immutable OWNER_MOD_STORAGE_SLOT; - - constructor(address _ownerModAddress) { - OWNER_MOD_STORAGE_SLOT = _ownerModAddress; - } - - function checkOwner() external view { - address currentOwner = IOwnerMod(OWNER_MOD_STORAGE_SLOT).owner(); - // ... use currentOwner ... - } - - function transferOwnershipToNew(address _newOwner) external { - IOwnerMod(OWNER_MOD_STORAGE_SLOT).transferOwnership(_newOwner); - } -}`} - - -## Best Practices - - -- Use `requireOwner()` judiciously to protect critical administrative functions. Ensure callers understand the implications of ownership changes. -- Handle `transferOwnership` with care, especially when setting the new owner to `address(0)` to renounce ownership. This action is irreversible. -- Be aware that `OwnerMod` relies on specific storage slot allocation. Do not alter the `STORAGE_POSITION` or storage struct layout without thorough testing and understanding of diamond upgrade implications. - - -## Integration Notes - - -The OwnerMod module utilizes a dedicated storage slot, defined by `STORAGE_POSITION`, to store its `OwnerModStorage` struct. Facets interacting with OwnerMod must be aware of this slot and use inline assembly via `getStorage()` to access the `OwnerModStorage` struct pointer. Changes to the owner are immediately reflected across all facets interacting with this storage slot. The `OwnerModStorage` struct should not be modified by other facets to maintain the integrity of the ownership mechanism. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx deleted file mode 100644 index ad87fba8..00000000 --- a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,304 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. - - - -- Implements a two-step ownership transfer process for enhanced security. -- Provides `requireOwner()` for robust access control checks within facets. -- Offers `renounceOwnership()` to permanently relinquish owner rights. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerTwoStepsMod provides a secure, two-step ownership transfer mechanism for Compose diamonds. This pattern prevents accidental or malicious ownership changes by requiring explicit acceptance from the new owner, enhancing contract safety and upgradeability. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -Storage position: `OWNER_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {OwnerTwoStepsMod} from "./OwnerTwoStepsMod.sol"; - -contract MyFacet is OwnerTwoStepsMod { - /** - * @custom:security-check requireOwner - */ - function sensitiveOperation() external { - requireOwner(); // Ensures only the owner can call this function - // ... perform sensitive operation ... - } - - function initiateOwnershipTransfer(address _newOwner) external { - transferOwnership(_newOwner); - } - - function acceptNewOwnership() external { - acceptOwnership(); // Call this when you are the pending owner - } - - function getCurrentOwner() external view returns (address) { - return owner(); - } - - function getPendingOwner() external view returns (address) { - return pendingOwner(); - } -}`} - - -## Best Practices - - -- Always use `requireOwner()` within facet functions that require administrative privileges to prevent unauthorized access. -- Implement `acceptOwnership()` in a separate transaction to complete the ownership transfer securely. -- Be aware that `renounceOwnership()` permanently removes owner privileges, so use it with extreme caution. - - -## Integration Notes - - -This module relies on specific storage slots for `Owner` and `PendingOwner`. Facets integrating this module will interact with these storage variables indirectly through the module's functions. Ensure that no other facets or modules attempt to write to these exact storage slots to maintain data integrity and prevent conflicts. The `getOwnerStorage` and `getPendingOwnerStorage` functions can be used to retrieve direct pointers to these storage locations if needed for advanced integration, but direct manipulation is discouraged. - - -
- -
- - diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx deleted file mode 100644 index ca3c56bb..00000000 --- a/website/docs/contracts/modules/RoyaltyMod.mdx +++ /dev/null @@ -1,365 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic." -gitSource: "https://github.com/maxnorm/Compose/blob/8fd3d0c28913baf3b948e02cb6af2111875ad106/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic. - - - -- Implements ERC-2981 standard for on-chain royalty information. -- Supports both default royalties applicable to all tokens and token-specific overrides. -- Provides a fallback mechanism to default royalties when token-specific royalties are not set. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The RoyaltyMod provides the necessary internal logic and storage structures to implement the ERC-2981 royalty standard within a Compose diamond. It enables setting and querying default and token-specific royalties, ensuring compliance and composability for NFT marketplaces and other royalty-dependent applications. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "./IRoyaltyMod.sol"; -import {RoyaltyStorage} from "./RoyaltyStorage.sol"; - -contract RoyaltyFacet { - // Assume RoyaltyMod is deployed and its address is known - address immutable royaltyModAddress; - - constructor(address _royaltyModAddress) { - royaltyModAddress = _royaltyModAddress; - } - - /** - * @notice Sets a default royalty for all tokens. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee percentage in basis points (e.g., 100 for 1%). - */ - function setDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { - IRoyaltyMod(royaltyModAddress).setDefaultRoyalty(_receiver, _feeBasisPoints); - } - - /** - * @notice Sets a specific royalty for a token. - * @param _tokenId The ID of the token. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee percentage in basis points. - */ - function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - IRoyaltyMod(royaltyModAddress).setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); - } - - /** - * @notice Queries royalty information for a given token and sale price. - * @param _tokenId The ID of the token. - * @param _salePrice The sale price of the token. - * @return receiver The address to receive royalties. - * @return feeBasisPoints The royalty fee percentage in basis points. - */ - function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint16 feeBasisPoints) { - return IRoyaltyMod(royaltyModAddress).royaltyInfo(_tokenId, _salePrice); - } -}`} - - -## Best Practices - - -- Ensure that the `setDefaultRoyalty` and `setTokenRoyalty` functions are protected by appropriate access control mechanisms to prevent unauthorized modifications. -- Always validate `_receiver` addresses and `_feeBasisPoints` to prevent zero or excessive fees being set, which could lead to unexpected behavior or gas inefficiencies. -- Be mindful of storage slot collisions if integrating custom storage for royalties; use `getStorage` to understand the current layout. - - -## Integration Notes - - -The RoyaltyMod utilizes a predefined storage slot to manage its `RoyaltyStorage` struct. Facets interacting with royalty logic must call the `getStorage` function to access this shared state. Changes made by `setDefaultRoyalty` and `setTokenRoyalty` are immediately visible to any facet calling `royaltyInfo` or `getStorage`. The storage layout of `RoyaltyStorage` should not be altered by other facets to maintain compatibility. - - -
- -
- - From 1de858238b2d3f178a9049fba53c7d999794721f Mon Sep 17 00:00:00 2001 From: maxnorm Date: Fri, 19 Dec 2025 22:49:02 +0000 Subject: [PATCH 34/68] docs: auto-generate docs pages from NatSpec --- .../contracts/facets/AccessControlFacet.mdx | 547 +++++++++++++ .../facets/AccessControlPausableFacet.mdx | 386 +++++++++ .../facets/AccessControlTemporalFacet.mdx | 459 +++++++++++ .../docs/contracts/facets/DiamondCutFacet.mdx | 445 +++++++++++ .../contracts/facets/DiamondLoupeFacet.mdx | 255 ++++++ .../docs/contracts/facets/ERC1155Facet.mdx | 682 ++++++++++++++++ .../contracts/facets/ERC20BridgeableFacet.mdx | 432 ++++++++++ .../docs/contracts/facets/ERC20BurnFacet.mdx | 260 ++++++ website/docs/contracts/facets/ERC20Facet.mdx | 571 +++++++++++++ .../contracts/facets/ERC20PermitFacet.mdx | 339 ++++++++ .../docs/contracts/facets/ERC6909Facet.mdx | 531 +++++++++++++ .../docs/contracts/facets/ERC721BurnFacet.mdx | 215 +++++ .../facets/ERC721EnumerableBurnFacet.mdx | 233 ++++++ .../facets/ERC721EnumerableFacet.mdx | 749 ++++++++++++++++++ website/docs/contracts/facets/ERC721Facet.mdx | 669 ++++++++++++++++ .../docs/contracts/facets/ExampleDiamond.mdx | 150 ++++ website/docs/contracts/facets/OwnerFacet.mdx | 213 +++++ .../contracts/facets/OwnerTwoStepsFacet.mdx | 292 +++++++ .../docs/contracts/facets/RoyaltyFacet.mdx | 199 +++++ .../contracts/modules/AccessControlMod.mdx | 451 +++++++++++ .../modules/AccessControlPausableMod.mdx | 405 ++++++++++ .../modules/AccessControlTemporalMod.mdx | 504 ++++++++++++ .../docs/contracts/modules/DiamondCutMod.mdx | 379 +++++++++ website/docs/contracts/modules/DiamondMod.mdx | 237 ++++++ website/docs/contracts/modules/ERC1155Mod.mdx | 616 ++++++++++++++ website/docs/contracts/modules/ERC165Mod.mdx | 162 ++++ .../contracts/modules/ERC20BridgeableMod.mdx | 438 ++++++++++ website/docs/contracts/modules/ERC20Mod.mdx | 426 ++++++++++ .../docs/contracts/modules/ERC20PermitMod.mdx | 296 +++++++ website/docs/contracts/modules/ERC6909Mod.mdx | 532 +++++++++++++ .../contracts/modules/ERC721EnumerableMod.mdx | 362 +++++++++ website/docs/contracts/modules/ERC721Mod.mdx | 359 +++++++++ .../contracts/modules/NonReentrancyMod.mdx | 152 ++++ website/docs/contracts/modules/OwnerMod.mdx | 258 ++++++ .../contracts/modules/OwnerTwoStepsMod.mdx | 307 +++++++ website/docs/contracts/modules/RoyaltyMod.mdx | 358 +++++++++ 36 files changed, 13869 insertions(+) create mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx create mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx create mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx create mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721Facet.mdx create mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx create mode 100644 website/docs/contracts/facets/OwnerFacet.mdx create mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx create mode 100644 website/docs/contracts/modules/AccessControlMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx create mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx create mode 100644 website/docs/contracts/modules/DiamondMod.mdx create mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx create mode 100644 website/docs/contracts/modules/ERC165Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx create mode 100644 website/docs/contracts/modules/ERC20Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx create mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx create mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx create mode 100644 website/docs/contracts/modules/ERC721Mod.mdx create mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx create mode 100644 website/docs/contracts/modules/OwnerMod.mdx create mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx create mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx new file mode 100644 index 00000000..ffe9929f --- /dev/null +++ b/website/docs/contracts/facets/AccessControlFacet.mdx @@ -0,0 +1,547 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Role-based access control (RBAC) facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control (RBAC) facet for Compose diamonds + + + +- Standardized RBAC implementation compatible with EIP-2678. +- Batch operations for granting and revoking roles to improve gas efficiency. +- Supports role hierarchy through role administration. + + +## Overview + +The AccessControlFacet implements a robust role-based access control (RBAC) system for Compose diamonds. It enables granular permission management by defining roles and assigning them to accounts, ensuring that only authorized entities can perform sensitive operations. This facet acts as a central authority for enforcing access policies across various diamond functionalities. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose/diamond-loupe/facets/DiamondLoupeFacet.sol"; +import {AccessControlFacet} from "@compose/access-control/facets/AccessControlFacet.sol"; + +contract Deployer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function grantAdminRoleToDeployer(address deployerAddress) external { + AccessControlFacet accessControl = AccessControlFacet(diamondAddress); + // Assume DEFAULT_ADMIN_ROLE is defined and accessible + // bytes32 DEFAULT_ADMIN_ROLE = accessControl.DEFAULT_ADMIN_ROLE(); // Hypothetical, actual constant needed + bytes32 adminRole = keccak256("DEFAULT_ADMIN_ROLE"); // Example role + accessControl.grantRole(adminRole, deployerAddress); + } + + function checkDeployerRole(address deployerAddress) external view returns (bool) { + AccessControlFacet accessControl = AccessControlFacet(diamondAddress); + bytes32 adminRole = keccak256("DEFAULT_ADMIN_ROLE"); // Example role + return accessControl.hasRole(adminRole, deployerAddress); + } +}`} + + +## Best Practices + + +- Initialize roles and grant administrative privileges during the diamond deployment process. +- Use `requireRole` within other facets to protect sensitive functions, ensuring calls are made by authorized roles. +- Manage role-to-role admin relationships carefully using `setRoleAdmin` to maintain a clear hierarchy. + + +## Security Considerations + + +Access to `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch` is restricted to the admin of the role being modified. `renounceRole` can only be called by the sender. Input validation is handled internally by the facet. Reentrancy is not a concern as these functions do not make external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..333c6ce4 --- /dev/null +++ b/website/docs/contracts/facets/AccessControlPausableFacet.mdx @@ -0,0 +1,386 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Role-based access control with pause functionality for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control with pause functionality for Compose diamonds + + + +- Role-specific pausing: Temporarily disable specific roles without affecting others. +- Admin-controlled operations: Only the designated admin for a role can pause or unpause it. +- Composable access control: Integrates seamlessly with Compose diamond's facet architecture. + + +## Overview + +The AccessControlPausableFacet integrates role-based access control with pause functionality into a Compose diamond. It allows for granular pausing and unpausing of specific roles, ensuring that sensitive operations can be temporarily halted by authorized administrators. This facet provides essential mechanisms for managing privileged actions within the diamond's ecosystem. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-protocol/diamond/contracts/interfaces/IDiamondCut.sol"; +import {AccessControlPausableFacet} from "@compose-protocol/diamond/facets/AccessControl/AccessControlPausableFacet.sol"; + +contract DeployDiamond { + address constant DIAMOND_CUT_FACET_ADDRESS = address(0x1); // Replace with actual address + address constant ACCESS_CONTROL_PAUSABLE_FACET_ADDRESS = address(0x2); // Replace with actual address + + function deploy() public { + // Assume diamondCutFacet is already deployed and initialized + IDiamondCut diamondCutFacet = IDiamondCut(DIAMOND_CUT_FACET_ADDRESS); + + // Deploy AccessControlPausableFacet and get its deployment address + // In a real scenario, you would use a factory or deploy directly + address accessControlPausableFacet = ACCESS_CONTROL_PAUSABLE_FACET_ADDRESS; // Placeholder + + // Define the functions to be added by this facet + Facet[] memory facetsToAdd = new Facet[](1); + facetsToAdd[0] = Facet({ + facetAddress: accessControlPausableFacet, + facetCuts: new FacetCut[]( + // Add all functions from AccessControlPausableFacet + // Example: addAccessControlPausableFacetFunctions(accessControlPausableFacet) + ) + }); + + // Note: Initialization logic for AccessControlPausableFacet (e.g., setting admin) would happen here + // or as a separate call after deployment. + + // diamondCutFacet.diamondCut(facetsToAdd, address(0), ""); + } + + // Helper to get function selectors (example, actual implementation depends on facet contract) + // function addAccessControlPausableFacetFunctions(address facetAddress) internal pure returns (FacetCut[] memory) { + // // ... logic to get selectors for getAccessControlStorage, getStorage, etc. ... + // } +}`} + + +## Best Practices + + +- Initialize the facet with the correct admin role to control pausing operations. +- Use `requireRoleNotPaused` proactively before calling sensitive functions that depend on role availability. +- Store the facet's storage structs in your diamond's storage layout, ensuring correct slotting and no overwrites. + + +## Security Considerations + + +Ensure that the `admin` role is appropriately managed and secured, as this role has the authority to pause and unpause critical functions. The `AccessControlUnauthorizedAccount` error prevents unauthorized callers from pausing/unpausing roles. The `AccessControlRolePaused` error guards against operations on paused roles. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..e20abb52 --- /dev/null +++ b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx @@ -0,0 +1,459 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Time-limited role-based access control for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Time-limited role-based access control for Compose diamonds + + + +- Time-limited role assignments that automatically expire. +- Explicit checks for role validity, considering expiry. +- Granular control over role lifecycles via admin-controlled granting and revoking. + + +## Overview + +The AccessControlTemporalFacet provides time-limited role-based access control for Compose diamonds. It allows granting roles that automatically expire and checking for role validity, enhancing dynamic permission management within the diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {AccessControlTemporalFacet} from "@compose/diamond-contracts/contracts/facets/AccessControlTemporalFacet.sol"; + +contract DeployAccessControlTemporal { + function deploy() public { + // Assume diamondProxy and admin are already deployed and initialized + address diamondProxy = address(0x...); // Address of your diamond proxy + address admin = address(0x...); // Address of the role admin + + // Deploy the facet + AccessControlTemporalFacet temporalFacet = new AccessControlTemporalFacet(); + address temporalFacetAddress = address(temporalFacet); + + // Prepare facet cut data + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: temporalFacetAddress, + action: IDiamondCut.FacetCutAction.ADD, + functionSelectors: AccessControlTemporalFacet.getSelectors() + }); + + // Add the facet to the diamond (requires admin role) + // IDiamondCut(diamondProxy).diamondCut(cut, address(0x0), ""); + + // Example of granting a role with expiry (requires role admin permission) + // uint64 expiryTimestamp = uint64(block.timestamp) + 3600; // 1 hour from now + // AccessControlTemporalFacet(diamondProxy).grantRoleWithExpiry(bytes32("ROLE_NAME"), address(0x1), expiryTimestamp); + + // Example of checking role validity + // bool isValid = AccessControlTemporalFacet(diamondProxy).isRoleExpired(bytes32("ROLE_NAME"), address(0x1)); + } +}`} + + +## Best Practices + + +- Initialize the diamond with the AccessControlFacet before adding this temporal facet to manage roles effectively. +- Ensure the role granting functions are called by the designated role admin to maintain security. +- Store role expiry timestamps appropriately to avoid accidental role expiration if the role is intended to be permanent. + + +## Security Considerations + + +Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role manipulation. The `requireValidRole` function correctly reverts if a role has expired or is not held, preventing unauthorized actions. Ensure proper management of role admin permissions to prevent privilege escalation. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx new file mode 100644 index 00000000..f59de0ee --- /dev/null +++ b/website/docs/contracts/facets/DiamondCutFacet.mdx @@ -0,0 +1,445 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Diamond upgrade (cut) facet for ERC-2535 diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond upgrade (cut) facet for ERC-2535 diamonds + + + +- Supports adding new facets and their functions to the diamond. +- Enables replacement of existing facet logic by updating function selectors. +- Allows for the removal of facets and their associated functions from the diamond. +- Provides access to the diamond's owner storage and the diamond's internal storage layout. + + +## Overview + +The DiamondCutFacet provides the core upgrade mechanism for ERC-2535 compliant diamonds. It allows authorized addresses to add, replace, or remove functions (facets) from the diamond proxy, enabling dynamic modification of the diamond's capabilities. This facet is crucial for managing the diamond's evolving logic and feature set. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +--- +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet} from "@compose/diamond/facets/DiamondCutFacet.sol"; +import {IDiamondCut} from "@compose/diamond/interfaces/IDiamondCut.sol"; + +contract DeployDiamond { + address constant DIAMOND_ADDRESS = address(0x1234567890abcdef); // Replace with your diamond address + + function upgradeDiamond() public { + DiamondCutFacet diamondCutFacet = DiamondCutFacet(DIAMOND_ADDRESS); + + // Example: Add a new facet + // Assume NewFacetABI is the ABI of the new facet contract + // Assume newFacetAddress is the deployed address of the new facet + // FunctionSelectors for the new facet functions are needed here + // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + // cut[0] = IDiamondCut.FacetCut({ + // facetAddress: newFacetAddress, + // action: IDiamondCut.FacetCutAction.ADD, + // functionSelectors: newFacetFunctionSelectors + // }); + // diamondCutFacet.diamondCut(cut, address(0), ""); + } + + function replaceFacetLogic() public { + // Example: Replace an existing facet + // Assume ExistingFacetABI and existingFacetAddress are known + // Assume selectorsToReplace are the selectors of functions to be replaced + // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + // cut[0] = IDiamondCut.FacetCut({ + // facetAddress: existingFacetAddress, + // action: IDiamondCut.FacetCutAction.REPLACE, + // functionSelectors: selectorsToReplace + // }); + // diamondCutFacet.diamondCut(cut, address(0), ""); + } + + function removeFacet() public { + // Example: Remove a facet + // Assume selectorsToRemove are the selectors of functions to be removed + // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + // cut[0] = IDiamondCut.FacetCut({ + // facetAddress: address(0), // Address is ignored when removing + // action: IDiamondCut.FacetCutAction.REMOVE, + // functionSelectors: selectorsToRemove + // }); + // diamondCutFacet.diamondCut(cut, address(0), ""); + } + + // Function to retrieve owner storage, requires knowledge of STORAGE_POSITION + function getOwnerStoragePointer() public view returns (address) { + return diamondCutFacet.getOwnerStorage(); + } +}`} + + +## Best Practices + + +- Ensure only authorized addresses can call `diamondCut` and its related functions. Access control should be managed externally or via another facet. +- Carefully construct `FacetCut` arrays to avoid unintended function removals or replacements. Audit selector lists before execution. +- Use `diamondCut` with caution, especially when replacing functions. Ensure new facet logic is compatible and thoroughly tested. + + +## Security Considerations + + +The `diamondCut` function is a critical administrative function. Unauthorized access can lead to the complete compromise of the diamond's functionality. Implement robust access control mechanisms to restrict its usage. Reentrancy is not directly applicable to `diamondCut` itself, but any `delegatecall` executed via `diamondCut` must be carefully audited for reentrancy vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..11cb7d4a --- /dev/null +++ b/website/docs/contracts/facets/DiamondLoupeFacet.mdx @@ -0,0 +1,255 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "The functions in DiamondLoupeFacet MUST be added to a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +The functions in DiamondLoupeFacet MUST be added to a diamond. + + + +- Provides functions to retrieve all facets and their associated function selectors within the diamond. +- Enables querying the specific facet address responsible for a given function selector. +- Offers an efficient way to list all unique facet addresses deployed in the diamond. +- Optimized for performance, using memory-based hash maps to reduce gas costs for complex diamonds. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query the diamond's internal structure, specifically identifying which facets are deployed, the functions they support, and the addresses they are mapped to. This facet is crucial for understanding and interacting with the diamond's composable architecture. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose/diamond-contracts/facets/DiamondLoupeFacet.sol"; +import {IDiamondLoupeFacet} from "@compose/diamond-contracts/facets/DiamondLoupeFacet.sol"; + +contract DiamondLoupeConsumer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getDiamondFacets() external view returns (IDiamondLoupeFacet.Facet[] memory) { + DiamondLoupeFacet loupe = DiamondLoupeFacet(diamondAddress); + return loupe.facets(); + } + + function getFacetAddress(bytes4 _selector) external view returns (address) { + DiamondLoupeFacet loupe = DiamondLoupeFacet(diamondAddress); + return loupe.facetAddress(_selector); + } +}`} + + +## Best Practices + + +- Integrate `DiamondLoupeFacet` into your diamond to enable runtime inspection of its facets and function mappings. +- Use the provided functions to dynamically discover facet addresses and their supported selectors, facilitating interaction with various diamond functionalities. +- Ensure that `DiamondLoupeFacet` is initialized with the correct diamond storage pointers during deployment. + + +## Security Considerations + + +This facet is primarily for introspection and does not modify state. Ensure that the diamond's upgrade mechanism correctly updates the facet mappings. Access control should be managed at the facet level, not within DiamondLoupeFacet itself, as it exposes internal diamond structure. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx new file mode 100644 index 00000000..a43f89e1 --- /dev/null +++ b/website/docs/contracts/facets/ERC1155Facet.mdx @@ -0,0 +1,682 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "ERC-1155 multi-token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-1155 multi-token facet for Compose diamonds + + + +- Supports both single token transfers and batched transfers for efficiency. +- Provides methods to query token ownership (`balanceOf`, `balanceOfBatch`) and operator approvals (`isApprovedForAll`). +- Implements URI resolution for tokens, allowing for dynamic metadata linking. +- Integrates seamlessly into the Compose diamond pattern, allowing for modular extension and upgradeability. + + +## Overview + +The ERC1155Facet provides a robust implementation for managing and transferring ERC-1155 multi-tokens within a Compose diamond. It handles token balances, approvals, and URI resolution, enabling a wide range of fungible and non-fungible token use cases. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut, IERC1155Facet} from "@compose/diamond-contracts/contracts/interfaces/IDiamond.sol"; +import {ERC1155Facet} from "@compose/diamond-contracts/contracts/facets/ERC1155Facet.sol"; + +contract DeployERC1155Diamond { + address diamondAddress; + + function deploy() public { + // Assume diamondAddress is already deployed and initialized + // ... deploy diamond proxy and set initial facets ... + + // Deploy the ERC1155Facet + ERC1155Facet erc1155Facet = new ERC1155Facet(); + + // Prepare diamond cut for adding the ERC1155Facet + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: address(erc1155Facet), + action: IDiamondCut.Action.ADD, + selectors: IDiamondCut.getSelectors(erc1155Facet) + }); + + // Call diamond loupe to cut the facet + // Assume diamondLoupe is the contract implementing IDiamondCut + // diamondLoupe.diamondCut(cut, address(0), ""); + + // For demonstration, directly call a function if diamondAddress is known + IERC1155Facet(diamondAddress).setApprovalForAll(msg.sender, true); + uint256 tokenId = 1; + uint256 amount = 100; + address owner = address(this); + address recipient = address(1); + // IERC1155Facet(diamondAddress).safeTransferFrom(owner, recipient, tokenId, amount, ""); + } +}`} + + +## Best Practices + + +- Initialize the ERC1155 storage correctly during diamond deployment to set the base URI and any initial token URIs. +- Implement access control mechanisms within your diamond's logic contract or separate facets to govern who can call administrative functions like `setApprovalForAll` or mint/burn operations (if implemented in custom facets). +- Ensure that any custom facets interacting with ERC1155 storage respect the storage layout and slot definitions of the `ERC1155Facet` to avoid conflicts. + + +## Security Considerations + + +This facet implements standard ERC-1155 functionality. Ensure that functions not exposed by this facet, such as minting or burning, are implemented in separate facets with appropriate access controls to prevent unauthorized token creation or destruction. Reentrancy is not a direct concern for the functions exposed by this facet itself, but downstream interactions with external contracts in `safeTransferFrom` and `safeBatchTransferFrom` should be audited for reentrancy vulnerabilities if the `to` address or `data` parameter leads to external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..855af877 --- /dev/null +++ b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx @@ -0,0 +1,432 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "ERC-20 token bridgeable facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token bridgeable facet for Compose diamonds + + + +- Enables cross-chain token transfers by allowing trusted bridges to mint and burn ERC-20 tokens. +- Integrates with the diamond's access control system to enforce authorization for bridging operations. +- Provides internal utility functions (`getERC20Storage`, `getAccessControlStorage`, `checkTokenBridge`) for interacting with diamond storage and access control. + + +## Overview + +The ERC20BridgeableFacet enables cross-chain minting and burning of ERC-20 tokens within a Compose diamond. It orchestrates token bridging operations by interacting with diamond storage and access control mechanisms to verify trusted bridge addresses. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20BridgeableFacet} from "@compose/contracts/facets/ERC20/ERC20BridgeableFacet.sol"; +import {AccessControlFacet} from "@compose/contracts/facets/AccessControl/AccessControlFacet.sol"; + +contract ERC20BridgeableFacetConsumer { + ERC20BridgeableFacet public erc20BridgeableFacet; + AccessControlFacet public accessControlFacet; + + constructor(address _diamondAddress) { + erc20BridgeableFacet = ERC20BridgeableFacet(_diamondAddress); + accessControlFacet = AccessControlFacet(_diamondAddress); + } + + /** + * @notice Example of minting tokens via the bridge. + * @param _token The ERC20 token address. + * @param _to The recipient address. + * @param _amount The amount to mint. + */ + function mintTokens(address _token, address _to, uint256 _amount) external { + // Assume the caller has the 'trusted-bridge' role. + // In a real scenario, access control would be enforced by the diamond proxy itself + // or by a separate caller with the appropriate role. + erc20BridgeableFacet.crosschainMint(_token, _to, _amount); + } + + /** + * @notice Example of burning tokens via the bridge. + * @param _token The ERC20 token address. + * @param _from The sender address. + * @param _amount The amount to burn. + */ + function burnTokens(address _token, address _from, uint256 _amount) external { + // Assume the caller has the 'trusted-bridge' role. + erc20BridgeableFacet.crosschainBurn(_token, _from, _amount); + } +}`} + + +## Best Practices + + +- Initialize the `ERC20BridgeableFacet` by adding it to the diamond proxy during deployment. +- Ensure the `trusted-bridge` role is correctly assigned to authorized bridge addresses in the `AccessControlFacet`. +- Use the `checkTokenBridge` internal function or rely on the diamond's access control mechanisms to verify bridge authorization before calling `crosschainMint` or `crosschainBurn`. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role. Ensure that only authorized and audited bridge contracts or addresses are granted this role to prevent unauthorized token minting or burning. The `checkTokenBridge` function explicitly verifies the caller's `trusted-bridge` role, mitigating risks from unauthorized callers. Reentrancy is not a direct concern for these mint/burn functions as they do not perform external calls back to untrusted contracts. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx new file mode 100644 index 00000000..d74214f5 --- /dev/null +++ b/website/docs/contracts/facets/ERC20BurnFacet.mdx @@ -0,0 +1,260 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "ERC-20 token burn facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token burn facet for Compose diamonds + + + +- Supports burning tokens from the caller's balance (`burn`). +- Supports burning tokens from another account's balance, respecting allowances (`burnFrom`). +- Integrates with the Compose diamond storage pattern for ERC-20 state management. + + +## Overview + +The ERC20BurnFacet provides functionality to burn ERC-20 tokens within a Compose diamond. It allows users to destroy tokens from their own balance or from another account's balance, reducing the total supply. This facet integrates seamlessly with the diamond's storage pattern for managing ERC-20 state. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BurnFacet} from "../facets/ERC20BurnFacet.sol"; +import {IDiamondCut} from "@compose/diamond-protocol/contracts/interfaces/IDiamondCut.sol"; + +contract ERC20BurnFacetDeployment { + address constant DIAMOND_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.erc20")))); + + function deployERC20BurnFacet() public returns (address facet) { + facet = address(new ERC20BurnFacet()); + + // Example: Add ERC20BurnFacet to the diamond + // IDiamondCut(diamondAddress).diamondCut(diamondCutCalldata, address(0), ""); + + return facet; + } + + // Example: Calling burnFrom + function burnSomeTokens(address diamondAddress, address _spender, address _from, uint256 _amount) public { + bytes4 selector = IERC20BurnFacet.burnFrom.selector; + + // Calldata for burnFrom + bytes memory data = abi.encodeWithSelector(selector, _from, _amount); + + // Assuming _spender has an allowance from _from + // Call the diamond proxy to execute burnFrom + (bool success, bytes memory returnData) = diamondAddress.call(data); + require(success, "Burn failed"); + } +}`} + + +## Best Practices + + +- Initialize the ERC20BurnFacet with the correct diamond storage slot address during deployment. +- Ensure the caller has sufficient allowance if using `burnFrom`. +- Access the facet through the diamond proxy address for all interactions. + + +## Security Considerations + + +The `burn` function is permissionless and reduces total supply. The `burnFrom` function requires proper allowance management to prevent unintended token burning. Ensure the diamond's access control mechanisms are correctly configured for any administrative functions related to token supply if applicable. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx new file mode 100644 index 00000000..f438b8fa --- /dev/null +++ b/website/docs/contracts/facets/ERC20Facet.mdx @@ -0,0 +1,571 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "ERC-20 fungible token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 fungible token facet for Compose diamonds + + + +- Full ERC-20 compliance, enabling seamless integration with wallets and DeFi protocols. +- Provides standard token metadata (`name`, `symbol`, `decimals`) and core transfer logic. +- Supports token approvals for third-party spending via `approve` and `transferFrom`. + + +## Overview + +The ERC20Facet implements the ERC-20 fungible token standard for Compose diamonds. It provides standard token operations like name, symbol, transfers, and approvals, making the diamond a compliant ERC-20 token. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Facet} from "@compose/contracts/src/facets/ERC20Facet.sol"; + +contract ERC20Deployer { + address immutable diamondProxy; + + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; + } + + function getTokenName() external view returns (string memory) { + // Selector for name() + bytes4 selector = IERC20Facet.name.selector; + (bool success, bytes memory data) = diamondProxy.call(abi.encodeWithSelector(selector)); + require(success, "ERC20Facet: name call failed"); + return abi.decode(data, (string)); + } + + function getTokenBalance(address _account) external view returns (uint256) { + // Selector for balanceOf() + bytes4 selector = IERC20Facet.balanceOf.selector; + (bool success, bytes memory data) = diamondProxy.call(abi.encodeWithSelector(selector, _account)); + require(success, "ERC20Facet: balanceOf call failed"); + return abi.decode(data, (uint256)); + } +}`} + + +## Best Practices + + +- Initialize the ERC20Facet with the correct ERC-20 storage slot during diamond deployment. +- Ensure appropriate access control is configured at the diamond level for sensitive functions like `approve` and `transferFrom` if necessary, though standard ERC-20 is typically permissionless. +- When upgrading, ensure the ERC20Facet's storage layout remains compatible to prevent data corruption. + + +## Security Considerations + + +Standard ERC-20 token risks apply, including potential reentrancy if custom logic interacts with `transfer` or `transferFrom` without proper checks. Input validation is handled internally by the facet. Ensure the diamond's access control layer does not inadvertently grant unauthorized access to administrative functions if they were to be added in the future. The `approve` function can be front-run; users should be aware of this standard ERC-20 behavior. The `getStorage` function uses inline assembly to access storage, which requires careful auditing to ensure correctness and prevent unintended state manipulation. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx new file mode 100644 index 00000000..40142735 --- /dev/null +++ b/website/docs/contracts/facets/ERC20PermitFacet.mdx @@ -0,0 +1,339 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "ERC-20 token permit facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token permit facet for Compose diamonds + + + +- Implements EIP-2612 `permit` functionality for gasless allowance approvals. +- Provides `nonces` to track the number of permit usages per owner, preventing replay attacks. +- Exposes `DOMAIN_SEPARATOR` for correct EIP-712 signature hashing. + + +## Overview + +The ERC20PermitFacet enables EIP-2612 compliant token permits within a Compose diamond. It allows users to grant token allowances to third parties via signed off-chain messages, reducing the need for direct on-chain approvals and improving user experience. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +--- +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import {ERC20PermitFacet} from "../facets/ERC20PermitFacet.sol"; + +contract ERC20PermitDeployment { + address public diamondAddress; + + function deploy() public { + // Assume diamondAddress is already set or deployed + diamondAddress = address(this); // Placeholder + + // In a real deployment, you would add the ERC20PermitFacet to the diamond. + // Example (conceptual): + // DiamondCutFacet(diamondAddress).diamondCut(...); + } + + function grantPermit(address _owner, address _spender, uint256 _value, uint256 _deadline, bytes calldata _signature) public { + // Assume the ERC20PermitFacet is already deployed and added to the diamond. + // The selector for permit is 0x6cc17c7b + (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(ERC20PermitFacet.permit.selector, _owner, _spender, _value, _deadline, _signature)); + require(success, "Permit call failed"); + } + + function getPermitNonces(address _owner) public view returns (uint256) { + // The selector for nonces is 0x151662e8 + (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(ERC20PermitFacet.nonces.selector, _owner)); + require(success, "Nonces call failed"); + return abi.decode(data, (uint256)); + } +}`} + + +## Best Practices + + +- Initialize the `DOMAIN_SEPARATOR` and `name`/`version` as part of the diamond's initialization process to ensure correct signature verification. +- Ensure the `ERC20PermitFacet` is added to the diamond before attempting to call its functions. +- Users must correctly construct the EIP-712 domain separator and message for signing, and provide a valid signature to the `permit` function. + + +## Security Considerations + + +The `permit` function relies on off-chain signatures. Ensure that the owner's private key is kept secure. The `deadline` parameter must be checked by the caller to prevent stale permits from being used. Reentrancy is not a concern for the `permit` function itself, as it only modifies allowances and nonces, and does not make external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx new file mode 100644 index 00000000..6c22ebc6 --- /dev/null +++ b/website/docs/contracts/facets/ERC6909Facet.mdx @@ -0,0 +1,531 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "ERC-6909 minimal multi-token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-6909 minimal multi-token facet for Compose diamonds + + + +- Implements a minimal ERC-6909 interface for multi-token management. +- Supports efficient querying of balances, allowances, and operator statuses. +- Enables core token operations: transfer, transferFrom, and approve. + + +## Overview + +The ERC6909Facet implements a minimal multi-token standard for Compose diamonds, enabling efficient management of various token types within a single diamond proxy. It provides essential functions for tracking balances, allowances, operator statuses, and executing token transfers and approvals. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Facet} from "@compose/contracts/src/facets/ERC6909/IERC6909Facet.sol"; +import {ERC6909Facet} from "@compose/contracts/src/facets/ERC6909/ERC6909Facet.sol"; + +contract ERC6909Diamond { + // ... diamond implementation ... + + function erc6909() public view returns (IERC6909Facet) { + // Replace with your diamond's selector mapping + address facetAddress = address(this); // Placeholder + return IERC6909Facet(facetAddress); + } + + function exampleUsage() public { + // Get balance of token ID 1 for the caller + uint256 balance = erc6909().balanceOf(msg.sender, 1); + + // Approve spender for token ID 2 + erc6909().approve(msg.sender, 2, 100); + + // Transfer token ID 3 from caller to a receiver + erc6909().transfer(msg.sender, receiver, 3, 50); + + // Set caller as an operator for token ID 4 + erc6909().setOperator(msg.sender, 4, true); + } +}`} + + +## Best Practices + + +- Initialize the ERC6909Facet with correct storage slot configurations during diamond deployment. +- Ensure that access control for functions like `setOperator` is handled appropriately by the diamond's access control mechanism. +- When upgrading, ensure the storage layout remains compatible according to EIP-2535. + + +## Security Considerations + + +Access control for sensitive functions like `transferFrom` and `approve` should be managed by the diamond's access control system. Ensure that the `setOperator` function does not grant excessive permissions unintentionally. Reentrancy is not a direct concern within this facet's functions as they do not make external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx new file mode 100644 index 00000000..14443fe2 --- /dev/null +++ b/website/docs/contracts/facets/ERC721BurnFacet.mdx @@ -0,0 +1,215 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "ERC-721 NFT burn facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 NFT burn facet for Compose diamonds + + + +- Enables the destruction of ERC721 tokens, permanently removing them from circulation. +- Integrates seamlessly with the Compose diamond storage pattern for ERC721 state management. +- Provides a dedicated function (`burn`) for token destruction, adhering to ERC721 standards. + + +## Overview + +The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens within a Compose diamond. It integrates with the diamond's storage pattern to manage token state and enumeration during the burn process. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "@compose/contracts/src/facets/ERC721/IERC721BurnFacet.sol"; + +contract ERC721BurnDiamondExample { + address constant BURN_FACET_ADDRESS = address(0x...); // Address of the deployed ERC721BurnFacet + + IERC721BurnFacet private _burnFacet; + + function initialize() external { + // Assuming the diamond proxy is already deployed and initialized with other facets + // Add the ERC721BurnFacet to the diamond proxy + // ... diamond.diamondCut(...) ... + _burnFacet = IERC721BurnFacet(BURN_FACET_ADDRESS); + } + + function burnToken(uint256 tokenId) external { + // Call the burn function through the diamond proxy + // In a real scenario, you would call this via the diamond proxy address + // For simplicity, directly calling the facet address here + _burnFacet.burn(tokenId); + } +}`} + + +## Best Practices + + +- Ensure the ERC721BurnFacet is correctly added to the diamond proxy during deployment or upgrade. +- Implement robust access control within your diamond's logic to restrict who can call the `burn` function, typically requiring ownership of the token. +- Use `getStorage()` if direct access to the ERC721 storage is needed for off-chain indexing or complex off-chain operations, understanding the storage slot. + + +## Security Considerations + + +Access control for the `burn` function is paramount. Ensure that only the owner of the token or an authorized entity can initiate a burn. The facet itself does not enforce ownership checks; this logic must be implemented in the calling contract or facet that routes to `burn`. Reentrancy is not a direct concern with the `burn` function as it does not make external calls after state changes. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..2a805201 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,233 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "ERC-721 NFT enumerableburn facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 NFT enumerableburn facet for Compose diamonds + + + +- Enables burning of ERC721 tokens directly on the diamond. +- Maintains internal token enumeration integrity after token destruction. +- Provides access to the facet's internal storage layout for advanced use cases. + + +## Overview + +The ERC721EnumerableBurnFacet extends ERC721 functionality by providing the ability to burn NFTs while maintaining enumeration tracking. It allows for the removal of tokens from the diamond's state and ensures that the internal token lists remain consistent. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondProxy} from "@compose-protocol/diamond-proxy/contracts/DiamondProxy.sol"; +import {IERC721EnumerableBurnFacet} from "./interfaces/IERC721EnumerableBurnFacet.sol"; + +contract Deployer { + function deploy() external { + // Assume diamondProxy is an already deployed DiamondProxy instance + DiamondProxy diamondProxy; + + // Get the facet implementation address (replace with actual deployment logic) + address erc721EnumerableBurnFacetImpl = address(0x...'); + + // Add the facet to the diamond + // (Requires DiamondCutFacet to be accessible and authorized) + // diamondProxy.diamondCut(...); + + // Interact with the facet through the diamond proxy + IERC721EnumerableBurnFacet enumerableBurnFacet = IERC721EnumerableBurnFacet(diamondProxy); + + // Example: Burn token ID 1 + address from = msg.sender; + uint256 tokenId = 1; + enumerableBurnFacet.burn(from, tokenId); + } +}`} + + +## Best Practices + + +- Ensure the ERC721EnumerableBurnFacet is correctly added to the diamond via a `diamondCut` operation before attempting to use its functions. +- The `burn` function requires the caller to be the owner of the token or an approved address, adhering to standard ERC721 authorization rules. +- Access the facet's storage struct using the `getStorage` function for introspection or debugging if necessary. + + +## Security Considerations + + +The `burn` function must enforce standard ERC721 ownership and approval checks to prevent unauthorized token destruction. Ensure that the diamond's access control mechanisms correctly delegate calls to this facet. Reentrancy is not a direct concern for the `burn` function itself, as it primarily modifies state and does not make external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..4e7c4b05 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx @@ -0,0 +1,749 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "ERC-721 NFT enumerable facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 NFT enumerable facet for Compose diamonds + + + +- Provides standard ERC-721 metadata (name, symbol, tokenURI). +- Tracks token ownership and balances efficiently. +- Supports both direct and safe token transfers, including receiver contract checks. +- Offers enumerable functions (`tokenOfOwnerByIndex`, `totalSupply`, `balanceOf`) for querying token collections. + + +## Overview + +The ERC721EnumerableFacet provides comprehensive ERC-721 functionality to a Compose diamond, including standard token metadata, ownership tracking, approvals, and enumerable methods to list tokens. It orchestrates the core state management for non-fungible tokens within the diamond's extensible architecture. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableFacet} from "@compose-protocol/diamond-contracts/facets/ERC721/IERC721EnumerableFacet.sol"; +import {DiamondProxy} from "@compose-protocol/diamond-contracts/DiamondProxy.sol"; + +contract ERC721EnumerableConsumer is DiamondProxy { + function mintToken(address _to, uint256 _tokenId) public { + // Assuming ERC721EnumerableFacet is already deployed and added to the diamond + // The function \`mintToken\` is not part of ERC721EnumerableFacet, but would be implemented + // in a custom facet that calls into internalTransferFrom if needed, or directly manages state. + // For demonstration, we assume a mechanism exists to set initial ownership. + + // Example of calling functions from the facet: + IERC721EnumerableFacet erc721 = IERC721EnumerableFacet(address(this)); + + // To actually mint, you'd typically have a dedicated minting facet + // that utilizes internalTransferFrom or similar internal logic. + // This example focuses on demonstrating calls to existing functions. + + // erc721.approve(_to, _tokenId); // Example approval + + // A placeholder for actual minting logic that would set owner and token IDs + // For a real mint, you would interact with the diamond's storage directly + // or via a dedicated minting facet. + + // Example: Querying token details + uint256 ownerTokenCount = erc721.balanceOf(_to); + // address owner = erc721.ownerOf(_tokenId); + // string memory uri = erc721.tokenURI(_tokenId); + } + + function getTokenOwnerByIndex(address _owner, uint256 _index) public view returns (uint256) { + IERC721EnumerableFacet erc721 = IERC721EnumerableFacet(address(this)); + return erc721.tokenOfOwnerByIndex(_owner, _index); + } +}`} + + +## Best Practices + + +- Initialize the ERC721EnumerableFacet with a name and symbol during diamond deployment or via an initialization function. +- Ensure appropriate access control is implemented in facets that call `approve`, `transferFrom`, or `safeTransferFrom` to prevent unauthorized token movements. +- When upgrading, maintain storage layout compatibility to avoid data corruption, especially for mapping and array structures. + + +## Security Considerations + + +This facet implements standard ERC-721 transfer logic. Ensure that the calling facets correctly validate `msg.sender` and approved addresses before invoking transfer functions (`transferFrom`, `safeTransferFrom`) to prevent unauthorized token transfers. Reentrancy is mitigated by the diamond's proxy pattern and typical ERC-721 implementation patterns where state changes precede external calls within a single function execution. Input validation for token IDs and addresses is crucial in any custom facets interacting with this facet. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx new file mode 100644 index 00000000..0afc3070 --- /dev/null +++ b/website/docs/contracts/facets/ERC721Facet.mdx @@ -0,0 +1,669 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "ERC-721 non-fungible token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 non-fungible token facet for Compose diamonds + + + +- Full ERC-721 compliance, enabling standard non-fungible token interactions. +- Supports both direct transfers and safe transfers, including checks for receiver contract compatibility. +- Provides essential query functions for token ownership, balances, and approvals. + + +## Overview + +The ERC721Facet provides a robust implementation of the ERC-721 non-fungible token standard within a Compose diamond. It enables the management and transfer of unique digital assets, exposing essential querying and mutation functions for token ownership, approvals, and metadata. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose-protocol/diamond/contracts/facets/ERC721Facet.sol"; + +contract ERC721Consumer { + address immutable DIAMOND_ADDRESS; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function getTokenName() external view returns (string memory) { + IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); + return erc721.name(); + } + + function getTokenSymbol() external view returns (string memory) { + IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); + return erc721.symbol(); + } + + function getTokenOwner(uint256 tokenId) external view returns (address) { + IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); + return erc721.ownerOf(tokenId); + } + + function approveToken(address to, uint256 tokenId) external { + IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); + erc721.approve(to, tokenId); + } +}`} + + +## Best Practices + + +- Ensure the ERC721Facet is correctly initialized with the appropriate storage slot during diamond deployment or upgrade. +- Utilize the `internalTransferFrom` function internally when implementing custom transfer logic to leverage built-in ownership and approval checks. +- Be mindful of gas costs when calling functions that iterate over token balances or approvals, especially for large token supplies. + + +## Security Considerations + + +The `safeTransferFrom` functions include checks to ensure the receiving address can handle ERC-721 tokens, mitigating risks associated with sending tokens to incompatible contracts. Direct transfers (`transferFrom`) do not perform this check. Access control for approval functions (`approve`, `setApprovalForAll`) is implicitly handled by ERC-721 ownership rules. Reentrancy is not a direct concern for the core ERC-721 functions themselves, but custom logic interacting with this facet should be audited for reentrancy vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx new file mode 100644 index 00000000..bded85ed --- /dev/null +++ b/website/docs/contracts/facets/ExampleDiamond.mdx @@ -0,0 +1,150 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Diamond core facet for ERC-2535 implementation" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond core facet for ERC-2535 implementation + + + +- Manages facet registration and function selector mapping according to ERC-2535. +- Acts as the central dispatcher, delegating calls to the correct facet via `delegatecall`. +- Supports Add, Replace, and Remove actions for facets during initialization and upgrades. + + +## Overview + +The ExampleDiamond contract serves as the core implementation of the ERC-2535 Diamond Standard. It manages facet registration, function selector mapping, and delegates calls to the appropriate facets, acting as the central routing mechanism for all diamond functionality. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ExampleDiamond} from "@compose/contracts/src/diamond/ExampleDiamond.sol"; +import {IDiamondCut} from "@compose/contracts/src/diamond/interfaces/IDiamondCut.sol"; + +// Assume other facets and their selectors are defined elsewhere +import {MyFacetA} from "./MyFacetA.sol"; +import {MyFacetB} from "./MyFacetB.sol"; + +contract DeployExampleDiamond { + address public diamondAddress; + + function deploy() public { + // Define facet cuts for deployment + IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](2); + + // Facet A cut + facetCuts[0] = IDiamondCut.FacetCut({ + facetAddress: address(new MyFacetA()), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: MyFacetA.getSelectors() + }); + + // Facet B cut + facetCuts[1] = IDiamondCut.FacetCut({ + facetAddress: address(new MyFacetB()), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: MyFacetB.getSelectors() + }); + + // Deploy the diamond, passing the initial facet cuts and owner + ExampleDiamond deployedDiamond = new ExampleDiamond(facetCuts, msg.sender); + diamondAddress = address(deployedDiamond); + } + + // Example of calling a function through the diamond + function callFacetA(address _diamondAddress) public { + // Assume MyFacetA has a function \`doSomething()\` + // The diamond's fallback or receive will handle routing + // This is illustrative; actual calls use the diamond's proxy address + (bool success, ) = _diamondAddress.call(abi.encodeWithSignature("doSomething()", MyFacetA.getSelectors()[0])); + require(success, "Call to Facet A failed"); + } +}`} + + +## Best Practices + + +- Initialize the diamond with all necessary facets during deployment using the `constructor` to ensure a functional state from the outset. +- Carefully manage the `FacetCutAction` enum (Add, Replace, Remove) to control facet updates during upgrades. +- Ensure that facet addresses provided during initialization are verified and trusted to prevent malicious code injection. + + +## Security Considerations + + +The constructor is critical for initial setup; ensure that only trusted facet addresses and selectors are provided. The `fallback` and `receive` functions are responsible for routing external calls, making them potential targets for reentrancy if not implemented carefully within the facets themselves. Input validation should be handled within individual facets, not the core diamond contract. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx new file mode 100644 index 00000000..6d5d56fa --- /dev/null +++ b/website/docs/contracts/facets/OwnerFacet.mdx @@ -0,0 +1,213 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Ownership management facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Ownership management facet for Compose diamonds + + + +- Provides a standard interface for diamond ownership management. +- Supports transferring ownership to a new address. +- Allows for the complete renouncement of ownership. + + +## Overview + +The OwnerFacet provides essential ownership management capabilities for a Compose diamond. It allows for the retrieval of the current owner and facilitates the transfer or renouncement of ownership, ensuring controlled administration of the diamond's core functions. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose-protocol/diamond-core/contracts/facets/Owner/IOwnerFacet.sol"; +import {DiamondProxy} from "@compose-protocol/diamond-core/contracts/diamond/DiamondProxy.sol"; + +contract OwnerFacetUser { + IOwnerFacet ownerFacet; + + constructor(address diamondProxyAddress) { + ownerFacet = IOwnerFacet(diamondProxyAddress); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function transferDiamondOwnership(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function renounceDiamondOwnership() external { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize ownership with a trusted address during diamond deployment. +- Use `transferOwnership` to designate a new owner and confirm the transfer by the new owner calling `transferOwnership` with their address. +- Grant `transferOwnership` and `renounceOwnership` permissions to a secure administrative role or the current owner. + + +## Security Considerations + + +Access to `transferOwnership` and `renounceOwnership` must be strictly controlled to prevent unauthorized changes to diamond administration. Ensure that the address set as the new owner is verified before the transfer is finalized. Renouncing ownership should be done with extreme caution as it permanently relinquishes control. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..1ceb5213 --- /dev/null +++ b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx @@ -0,0 +1,292 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Two-step ownership transfer facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Two-step ownership transfer facet for Compose diamonds + + + +- Implements a secure two-step ownership transfer mechanism. +- Allows querying of current owner and pending owner addresses. +- Provides explicit functions for `transferOwnership`, `acceptOwnership`, and `renounceOwnership`. + + +## Overview + +The OwnerTwoStepsFacet manages the ownership of a Compose diamond through a secure two-step transfer process. It provides functions to view the current and pending owner, initiate a transfer, and accept or renounce ownership, ensuring robust control over administrative privileges. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +--- +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "@compose/diamond/contracts/facets/ownership/IOwnerTwoStepsFacet.sol"; +import {DiamondProxy} from "@compose/diamond/contracts/DiamondProxy.sol"; + +contract OwnerTwoStepsFacetUser { + IOwnerTwoStepsFacet ownerFacet; + + constructor(address diamondProxyAddress) { + ownerFacet = IOwnerTwoStepsFacet(diamondProxyAddress); + } + + function transferOwnershipTo(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function acceptOwnershipFrom(address _currentOwner) external { + ownerFacet.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function getPendingOwner() external view returns (address) { + return ownerFacet.pendingOwner(); + } +}`} + + +## Best Practices + + +- Initialize ownership transfers using `transferOwnership` and confirm with `acceptOwnership` to prevent accidental loss of control. +- Ensure the diamond proxy address is correctly set when interacting with the facet. +- Use `renounceOwnership` only when the contract is intended to become unowned. + + +## Security Considerations + + +The `transferOwnership` function sets a pending owner. The ownership is only fully transferred once the new owner calls `acceptOwnership`. This prevents ownership from being transferred to an incorrect or inaccessible address. There are no reentrancy concerns as these functions do not make external calls. Input validation is handled by the Solidity type system for addresses. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx new file mode 100644 index 00000000..c597b924 --- /dev/null +++ b/website/docs/contracts/facets/RoyaltyFacet.mdx @@ -0,0 +1,199 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "ERC-2981 royalty facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-2981 royalty facet for Compose diamonds + + + +- Implements the ERC-2981 `royaltyInfo` standard. +- Supports token-specific royalty configurations. +- Provides a fallback to a default royalty setting. +- Utilizes inline assembly for efficient storage access. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, enabling composable royalty payments within a Compose diamond. It provides a standardized interface for querying royalty information for specific tokens, facilitating revenue sharing for creators and secondary market participants. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyFacet} from "@compose/core/facets/RoyaltyFacet.sol"; +import {IDiamond} from "@compose/core/interfaces/IDiamond.sol"; + +contract RoyaltyConsumer { + IDiamond immutable diamondProxy; + + constructor(address _diamondProxy) { + diamondProxy = IDiamond(_diamondProxy); + } + + function getTokenRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { + // Get the RoyaltyFacet selector + bytes4 royaltySelector = IRoyaltyFacet.royaltyInfo.selector; + + // Call the royaltyInfo function through the diamond proxy + (bool success, bytes memory result) = address(diamondProxy).call(abi.encodeWithSelector(royaltySelector, _tokenId, _salePrice)); + require(success, "RoyaltyFacet: royaltyInfo call failed"); + + // Decode the result + (receiver, royaltyAmount) = abi.decode(result, (address, uint256)); + return (receiver, royaltyAmount); + } +}`} + + +## Best Practices + + +- Initialize the RoyaltyFacet with appropriate default royalty settings during diamond deployment. +- Ensure that token-specific royalty configurations are set correctly using the underlying storage mechanism. +- When upgrading, preserve the `STORAGE_POSITION` for the royalty storage struct to maintain state continuity. + + +## Security Considerations + + +The `royaltyInfo` function is read-only and does not introduce reentrancy risks. Access control for setting default and token-specific royalties should be managed at the diamond level or through a dedicated administrative facet. Ensure the `STORAGE_POSITION` constant is unique and does not conflict with other facets. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx new file mode 100644 index 00000000..fc806118 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlMod.mdx @@ -0,0 +1,451 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Role-based access control (RBAC) module for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control (RBAC) module for Compose diamonds + + + +- Standardized RBAC implementation for consistent permission management across facets. +- Functions for granting, revoking, and checking roles, as well as setting role administrators. +- Built-in check (`requireRole`) that reverts with a specific error on unauthorized access. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlMod provides robust role-based access control (RBAC) for Compose diamonds. It enables fine-grained permission management, ensuring that only authorized accounts can execute critical functions, thereby enhancing the security and integrity of diamond operations. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlMod} from "@compose/modules/AccessControlMod.sol"; + +contract MyFacet { + IAccessControlMod internal accessControlMod; + + constructor(address _accessControlModAddress) { + accessControlMod = IAccessControlMod(_accessControlModAddress); + } + + // Example role: DEFAULT_ADMIN_ROLE + bytes32 public constant DEFAULT_ADMIN_ROLE = keccak256("DEFAULT_ADMIN_ROLE"); + bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); + + function grantManagerRole(address _account) external { + // Only the admin can grant the manager role + accessControlMod.requireRole(_account, DEFAULT_ADMIN_ROLE); + accessControlMod.grantRole(MANAGER_ROLE, _account); + } + + function performManagerAction() external { + // Only users with the MANAGER_ROLE can perform this action + accessControlMod.requireRole(msg.sender, MANAGER_ROLE); + // ... manager action logic ... + } +}`} + + +## Best Practices + + +- Always use custom errors provided by the AccessControlMod for revert conditions to ensure gas efficiency and clarity. +- When defining roles, use `keccak256` on a descriptive string for immutability and uniqueness. +- Ensure the AccessControlMod is initialized with appropriate admin roles during deployment to secure the access control system itself. + + +## Integration Notes + + +The AccessControlMod utilizes its own dedicated storage slot within the diamond. Facets interact with the module via its interface. Changes to role assignments or role admin configurations are immediately reflected and visible to all facets querying the module's functions. When adding the AccessControlMod as a facet, ensure its storage is initialized correctly and that the `DEFAULT_ADMIN_ROLE` is assigned to the appropriate deployer or owner account. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx new file mode 100644 index 00000000..da0dfd98 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlPausableMod.mdx @@ -0,0 +1,405 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Role-based access control with pause functionality for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control with pause functionality for Compose diamonds + + + +- Role-based authorization: Enforces that only accounts assigned specific roles can execute protected functions. +- Pause functionality: Allows for temporary suspension of role execution, providing an emergency stop mechanism. +- Diamond-native integration: Designed to seamlessly integrate with the Compose diamond proxy pattern and its storage management. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides robust role-based access control combined with pause functionality for Compose diamonds. It ensures that sensitive operations can be restricted to authorized roles and temporarily halted when necessary, enhancing security and operational control within a diamond. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableMod} from "@compose/diamond-contracts/contracts/modules/AccessControlPausableMod.sol"; + +contract MyFacet { + // Assuming AccessControlPausableMod is deployed at this address + IAccessControlPausableMod internal accessControlPausableMod; + + constructor(address _accessControlPausableModAddress) { + accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableModAddress); + } + + /** + * @notice Pauses a specific role, preventing any further execution of functions protected by that role. + * @param _role The role to pause. + */ + function pauseMyRole(bytes32 _role) external { + // Example: Only an admin can pause a role + // require(msg.sender == diamond.owner(), \"Not owner\"); + accessControlPausableMod.pauseRole(_role); + } + + /** + * @notice Unpauses a specific role, allowing functions protected by that role to be executed again. + * @param _role The role to unpause. + */ + function unpauseMyRole(bytes32 _role) external { + // Example: Only an admin can unpause a role + // require(msg.sender == diamond.owner(), \"Not owner\"); + accessControlPausableMod.unpauseRole(_role); + } + + /** + * @notice Checks if a given role is currently paused. + * @param _role The role to check. + * @return bool True if the role is paused, false otherwise. + */ + function isMyRolePaused(bytes32 _role) external view returns (bool) { + return accessControlPausableMod.isRolePaused(_role); + } + + /** + * @notice Requires that an account has a specific role and that the role is not currently paused. + * @param _role The role to check. + * @param _account The account to check. + */ + function executeActionWithRole(bytes32 _role, address _account) external { + accessControlPausableMod.requireRoleNotPaused(_role, _account); + // ... execute sensitive action ... + } +} +`} + + +## Best Practices + + +- Ensure that only authorized entities can call `pauseRole` and `unpauseRole` functions, typically through an admin role managed by the diamond's ownership pattern. +- Thoroughly test the `requireRoleNotPaused` function in conjunction with your facet's access-controlled logic to prevent unauthorized or paused role executions. +- Be mindful of upgradeability: changes to the underlying storage layout of this module may require careful migration strategies to maintain state consistency across diamond upgrades. + + +## Integration Notes + + +This module interacts with two distinct storage areas within the diamond: `AccessControl` storage and `AccessControlPausable` storage. Facets that utilize this module will typically call its public functions. The `requireRoleNotPaused` function performs checks against both role membership and pause status, reverting with specific errors (`AccessControlUnauthorizedAccount`, `AccessControlRolePaused`) if conditions are not met. Facets should ensure they have access to the correct storage slots for these internal structs if they need to directly inspect or manipulate role assignments or pause states. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..e7fba01e --- /dev/null +++ b/website/docs/contracts/modules/AccessControlTemporalMod.mdx @@ -0,0 +1,504 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Time-limited role-based access control for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Time-limited role-based access control for Compose diamonds + + + +- Time-limited role assignments: Grants roles that automatically expire after a specified timestamp. +- Temporal role revocation: Allows for immediate removal of a role before its expiry. +- Role expiry checking: Provides functions to query the expiry status of role assignments. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlTemporalMod provides time-limited role-based access control, enabling granular permission management within Compose diamonds. This module is crucial for scenarios requiring temporary privileges, enhancing security and operational flexibility by automatically revoking access after a specified duration. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/AccessControlTemporalMod/IAccessControlTemporalMod.sol"; +import {DiamondStorage, DiamondFacet, AccessControlUnauthorizedAccount, AccessControlRoleExpired} from "@compose/core/"; + +contract MyFacet is DiamondFacet { + IAccessControlTemporalMod internal constant accessControlTemporalMod = IAccessControlTemporalMod(address(this)); + + // Role definition (example) + bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + + /** + * @notice Grants an operator role to an address with a specific expiry. + * @param _account The address to grant the role to. + * @param _expiry The timestamp when the role expires. + */ + function grantOperatorRole(address _account, uint64 _expiry) external { + accessControlTemporalMod.grantRoleWithExpiry(OPERATOR_ROLE, _account, _expiry); + } + + /** + * @notice Revokes a temporal role from an address. + * @param _role The role to revoke. + * @param _account The address to revoke the role from. + */ + function revokeOperatorRole(bytes32 _role, address _account) external { + accessControlTemporalMod.revokeTemporalRole(_role, _account); + } + + /** + * @notice Requires that the caller has a valid, non-expired operator role. + */ + function performOperation() external { + accessControlTemporalMod.requireValidRole(OPERATOR_ROLE, msg.sender); + // Operation logic here + } + + /** + * @notice Checks if a role has expired. + * @param _role The role to check. + * @param _account The account assigned the role. + * @return bool True if the role has expired, false otherwise. + */ + function checkRoleExpiry(bytes32 _role, address _account) external view returns (bool) { + return accessControlTemporalMod.isRoleExpired(_role, _account); + } +} +`} + + +## Best Practices + + +- Always use `requireValidRole` to enforce temporal access before critical operations, handling `AccessControlUnauthorizedAccount` and `AccessControlRoleExpired` errors. +- When granting roles, ensure the `_expiry` timestamp is set appropriately to prevent indefinite access and manage temporary permissions effectively. +- Use `revokeTemporalRole` for immediate revocation of roles before their natural expiry if circumstances change. + + +## Integration Notes + + +The AccessControlTemporalMod interacts with the diamond's storage to manage role assignments and their expiry timestamps. Facets using this module will typically call its external functions. The module's storage is distinct and managed independently, but its state (role assignments and expiry) directly impacts the access control checks performed by facets. Ensure the `AccessControlTemporalMod` facet is correctly initialized and accessible within the diamond's facet registry. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx new file mode 100644 index 00000000..c838e3e7 --- /dev/null +++ b/website/docs/contracts/modules/DiamondCutMod.mdx @@ -0,0 +1,379 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Diamond upgrade (cut) module for ERC-2535 diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond upgrade (cut) module for ERC-2535 diamonds + + + +- Supports adding, replacing, and removing functions via function selectors and facet addresses. +- Allows for atomic upgrades by enabling the execution of a function immediately after the cut operation via `delegatecall`. +- Provides a mechanism to retrieve the storage layout of the diamond. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCutMod provides essential functionality for managing facets within an ERC-2535 Diamond Proxy. It enables developers to add, replace, and remove functions, facilitating upgrades and modularity. This module is crucial for dynamic diamond evolution, allowing for safe and controlled modifications to the diamond's behavior. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/contracts/src/diamond/IDiamondCut.sol"; + +contract MyFacet { + address constant DIAMOND_CUT_FACET_ADDRESS = address(0x1234567890abcdef1234567890abcdef1234567890); // Replace with actual address + IDiamondCut immutable diamondCutFacet; + + constructor(address _diamondCutFacetAddress) { + diamondCutFacet = IDiamondCut(_diamondCutFacetAddress); + } + + function upgradeFacet(bytes4[] memory _selectors, address _newFacetAddress) external { + // Assuming this facet has the necessary permissions to call diamondCut + // For demonstration, we are only replacing functions, not adding/removing + diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](1)[]({ + facetAddress: _newFacetAddress, + action: IDiamondCut.FacetAction.Replace, + selectors: _selectors + }), address(0), ""); + } +}`} + + +## Best Practices + + +- Ensure that any facet calling `diamondCut` has the appropriate access control (e.g., is an owner or authorized role) as this function can significantly alter the diamond's functionality. +- Carefully manage function selectors when adding, replacing, or removing them to avoid unintended behavior or orphaned functions. +- Understand the implications of `diamondCut`'s `execute` functionality; only use it with trusted functions and data, as it performs a `delegatecall`. + + +## Integration Notes + + +The DiamondCutMod interacts directly with the diamond proxy's internal storage to map function selectors to facet addresses. Changes made through `diamondCut` are immediately reflected in the diamond's routing logic. Facets that query or rely on the diamond's function routing will automatically see the updated mappings after a successful diamond cut operation. The order of facet additions and removals is critical for maintaining correct storage layout and function accessibility. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx new file mode 100644 index 00000000..999fa468 --- /dev/null +++ b/website/docs/contracts/modules/DiamondMod.mdx @@ -0,0 +1,237 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Diamond Library - Internal functions and storage for diamond proxy functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond Library - Internal functions and storage for diamond proxy functionality. + + + +- Manages facet registration and function selector mapping during diamond deployment (`addFacets`). +- Provides a fallback mechanism (`diamondFallback`) to route external calls to the appropriate facet. +- Allows read access to raw storage slots of the diamond proxy (`getStorage`). + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondMod library provides essential internal functions for managing diamond proxy facets and handling function calls. It is crucial for the composition and operational integrity of a Compose diamond, enabling dynamic facet registration and ensuring correct function dispatch. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondMod} from "@compose/diamond/DiamondMod.sol"; + +contract MyFacet { + DiamondMod internal immutable diamondMod; + + constructor(address _diamondModAddress) { + diamondMod = DiamondMod(_diamondModAddress); + } + + /** + * @notice Example of calling a function within DiamondMod to retrieve storage. + * @return The raw storage slot value. + */ + function readDiamondStorage(uint256 _slot) external view returns (bytes32) { + return diamondMod.getStorage(_slot); + } +}`} + + +## Best Practices + + +- Use `DiamondMod` only during initial diamond deployment for `addFacets`. Its functions are intended for internal proxy operations, not direct external facet interaction after deployment. +- Ensure correct initialization of `DiamondMod` address within facets that require access to diamond proxy state or logic. +- Handle potential errors during function execution via `diamondFallback` if custom error handling is required by your facet logic. + + +## Integration Notes + + +DiamondMod interacts directly with the diamond proxy's storage. The `addFacets` function is designed to be called only during the diamond's initial deployment to register facets and their function selectors. `diamondFallback` is the core dispatch mechanism, finding the correct facet for any incoming call not handled by the proxy itself. `getStorage` provides a low-level view into the diamond's state, directly accessing specified storage slots. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx new file mode 100644 index 00000000..0e61e399 --- /dev/null +++ b/website/docs/contracts/modules/ERC1155Mod.mdx @@ -0,0 +1,616 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens. + + + +- Supports both single and batch transfers of ERC-1155 tokens, ensuring flexibility for various asset types. +- Implements safe transfer logic, including receiver validation for contract addresses, adhering to EIP-1155 standards. +- Provides functionality for setting token URIs, enabling rich metadata for each token ID. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod provides a robust implementation of the ERC-1155 Multi-Token Standard, enabling facets to manage fungible and non-fungible tokens within a Compose diamond. This module is crucial for creating complex economies and managing diverse digital assets, ensuring safe transfers and clear metadata handling. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {ERC1155Storage, ERC1155Facet} from "./facets/ERC1155Facet.sol"; + +contract MyERC1155Consumer is IERC1155Receiver { + address diamondAddress; + ERC1155Facet erc1155Facet; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + erc1155Facet = ERC1155Facet(diamondAddress); + } + + function mintTokens(address _to, uint256 _id, uint256 _amount) external { + erc1155Facet.mint(_to, _id, _amount); + } + + function safeTransfer(address _from, address _to, uint256 _id, uint256 _amount) external { + erc1155Facet.safeTransferFrom(_from, _to, _id, _amount, ""); + } + + // Implement IERC1155Receiver functions as needed + function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) { + return IERC1155Receiver.onERC1155Received.selector; + } + + function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) { + return IERC1155Receiver.onERC1155BatchReceived.selector; + } +}`} + + +## Best Practices + + +- Always validate receiver addresses in `safeTransferFrom` and `safeBatchTransferFrom` to prevent unexpected behavior or reentrancy. +- Ensure proper access control is implemented at the diamond level for functions like `setBaseURI` and `setTokenURI` if they require administrative privileges. +- Be mindful of gas costs when minting or transferring large batches of tokens; consider batching operations where appropriate. + + +## Integration Notes + + +The ERC1155Mod interacts with diamond storage through a predefined storage slot managed by the diamond proxy. Facets using this module will access and modify state variables such as balances, approvals, and token URIs. Changes to these storage variables are directly visible to all facets interacting with the ERC1155 storage struct. The `getStorage` function provides direct access to this struct, allowing for read operations on the ERC-1155 state. Ensure that the ERC1155 storage struct is correctly laid out and initialized within the diamond's storage pattern. Avoid modifying storage slots used by other facets to maintain composability and prevent conflicts. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx new file mode 100644 index 00000000..087941e3 --- /dev/null +++ b/website/docs/contracts/modules/ERC165Mod.mdx @@ -0,0 +1,162 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. + + + +- Provides a standardized mechanism for interface detection via ERC-165. +- Stores interface support data efficiently within the diamond's storage. +- Enables composability by clearly communicating supported functionalities to external agents. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod module provides the necessary storage and logic to comply with the ERC-165 standard for interface detection within a Compose diamond. This is crucial for allowing external contracts to query which interfaces a diamond supports, enhancing interoperability and composability. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC165, IERC165, ERC165Mod} from "@compose/modules/ERC165/LibERC165.sol"; + +contract MyERC721Facet { + /** + * @dev Initializes the ERC721 facet and registers supported interfaces. + */ + function initERC721() external { + // Register the ERC721 interface ID + ERC165Mod.registerInterface(type(IERC721).interfaceId); + + // Other initialization logic for ERC721 facet + } + + /** + * @dev Implements the supportsInterface function from ERC165. + */ + function supportsInterface(bytes4 interfaceId) external view virtual override(IERC165) returns (bool) { + // Check if the interface is registered in the ERC165 storage + return LibERC165.supportsInterface(interfaceId); + } +}`} + + +## Best Practices + + +- Call `ERC165Mod.registerInterface()` during facet initialization to declare supported interfaces. +- Ensure the `supportsInterface` function within your facet correctly delegates to `LibERC165.supportsInterface()`. +- Keep the list of registered interfaces accurate to avoid misleading callers about diamond capabilities. + + +## Integration Notes + + +The ERC165Mod utilizes a dedicated storage slot for its `ERC165Storage` struct. This struct contains a mapping from interface IDs to booleans, indicating support. Facets should call `ERC165Mod.registerInterface()` during their initialization phase to populate this mapping. The `supportsInterface` function, typically implemented in a facet, should query this storage via `LibERC165.supportsInterface()` to return accurate results. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..327434b0 --- /dev/null +++ b/website/docs/contracts/modules/ERC20BridgeableMod.mdx @@ -0,0 +1,438 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "LibERC20Bridgeable — ERC-7802 Library" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Bridgeable — ERC-7802 Library + + + +- Enforces access control for cross-chain operations via the `TrustedBridge` role. +- Provides explicit functions for cross-chain token burning and minting. +- Leverages internal helper functions (`checkTokenBridge`, `getAccessControlStorage`, `getERC20Storage`) for efficient and direct access to necessary storage. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides cross-chain ERC20 token bridging functionality, enabling secure burning and minting across different chains. It enforces access control by relying on a 'trusted-bridge' role, ensuring only authorized entities can perform cross-chain operations. This is crucial for maintaining the integrity and security of token transfers in a multi-chain environment. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Bridgeable } from "@compose-protocol/diamond-contracts/contracts/modules/erc20/interfaces/IERC20Bridgeable.sol"; +import { LibAccessControl } from "@compose-protocol/diamond-contracts/contracts/modules/access/LibAccessControl.sol"; + +contract MyFacet is IERC20Bridgeable { + using LibAccessControl for LibAccessControl.AccessControlStorage; + + function crosschainBurn(address _token, address _from, uint256 _amount) external { + // This function is directly callable from the diamond proxy. + // The actual implementation is in the ERC20Bridgeable facet. + // Ensure your facet has a selector for this function. + + // Example of calling the diamond's implementation: + // This is illustrative; direct calls from facets to other facets + // are typically not needed for core functionality. + // The diamond proxy routes calls to the correct facet. + + // To demonstrate the checkTokenBridge logic internally: + LibAccessControl.AccessControlStorage storage acs = LibAccessControl.getAccessControlStorage(); + acs.checkRole(LibAccessControl.Role.TrustedBridge); + + // In a real scenario, the diamond proxy would route this call + // to the ERC20Bridgeable facet's crosschainBurn function. + // The facet itself doesn't need to explicitly call other facets for its own functions. + } + + function crosschainMint(address _token, address _to, uint256 _amount) external { + // Similar to crosschainBurn, the diamond proxy routes this. + LibAccessControl.AccessControlStorage storage acs = LibAccessControl.getAccessControlStorage(); + acs.checkRole(LibAccessControl.Role.TrustedBridge); + } + + // Other facet functions would go here... +}`} + + +## Best Practices + + +- Ensure that only addresses assigned the `TrustedBridge` role can call `crosschainBurn` and `crosschainMint` functions. This role management is handled by the AccessControl module. +- When upgrading facets, be mindful of the storage layout of `AccessControlStorage` and `ERC20Storage` to maintain compatibility and prevent data corruption. +- Implement robust error handling by checking the return values or using custom errors for revert conditions, such as an untrusted bridge caller. + + +## Integration Notes + + +The `ERC20BridgeableMod` interacts with the diamond's storage through two primary storage structs: `AccessControlStorage` and `ERC20Storage`. The `getAccessControlStorage` and `getERC20Storage` functions provide direct access to these structs at their predefined diamond storage slots. The `checkTokenBridge` internal function specifically relies on the `AccessControlStorage` to verify the caller's `TrustedBridge` role. Facets implementing or interacting with this module should ensure they have the correct selectors registered for `crosschainBurn` and `crosschainMint` and that the diamond's storage layout accommodates these structs without conflicts. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx new file mode 100644 index 00000000..13a266da --- /dev/null +++ b/website/docs/contracts/modules/ERC20Mod.mdx @@ -0,0 +1,426 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic. + + + +- Provides essential ERC-20 functions: `mint`, `burn`, `transfer`, `transferFrom`, and `approve`. +- Manages ERC-20 token supply and balances internally. +- Uses inline assembly via `getStorage` for efficient access to the ERC-20 storage layout, adhering to the diamond storage pattern. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod module provides a standardized internal library for ERC-20 token functionalities. It manages essential token state and operations like minting, burning, transfers, and approvals, enabling composable and upgradeable ERC-20 implementations within a diamond proxy. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "../modules/erc20/interfaces/IERC20Mod.sol"; +import { ERC20Storage } from "../modules/erc20/storage/ERC20Storage.sol"; + +contract MyERC20Facet { + IERC20Mod private constant _ERC20 = IERC20Mod(address(this)); + + // Assume _ERC20.getStorage() correctly resolves to the ERC20Storage struct + // within the diamond's storage layout. + + function mintTokens(address _to, uint256 _amount) external { + // Ensure caller has permission to mint, e.g., via an access control facet + _ERC20.mint(_to, _amount); + } + + function transferTokens(address _from, address _to, uint256 _amount) external { + // Ensure caller has permission to transfer from _from + _ERC20.transferFrom(_from, _to, _amount); + } + + function approveSpender(address _spender, uint256 _amount) external { + _ERC20.approve(msg.sender, _spender, _amount); + } +}`} + + +## Best Practices + + +- Always ensure that access control is handled by a separate facet or within the calling facet before invoking ERC20Mod functions that modify state (e.g., mint, burn, transfer). +- When extending ERC20Mod, ensure new storage variables are added to the end of the `ERC20Storage` struct to maintain compatibility with existing deployments. +- Handle potential `ERC20Errors` (if defined by the specific ERC-20 facet implementation) or check return values for transfer functions to gracefully manage failed operations. + + +## Integration Notes + + +The ERC20Mod library interacts directly with the `ERC20Storage` struct, which must be allocated to a specific storage slot within the diamond proxy's overall storage layout. The `getStorage` function utilizes inline assembly to bind to this fixed slot. Facets that use ERC20Mod must ensure the `ERC20Storage` struct is correctly defined and positioned in the diamond's storage blueprint, and that its internal layout matches the library's expectations. Changes to the `ERC20Storage` struct by other facets could break ERC20Mod functionality if not managed carefully. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx new file mode 100644 index 00000000..1d2ce4db --- /dev/null +++ b/website/docs/contracts/modules/ERC20PermitMod.mdx @@ -0,0 +1,296 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage" +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage + + + +- Implements ERC-2612 permit functionality, enabling gasless token approvals. +- Manages domain separator generation for secure signature validation. +- Provides a clear interface for validating and applying permit signatures. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20PermitMod provides essential ERC-2612 permit functionality, enabling gasless approvals for ERC-20 token transfers. It manages domain separator calculation and permit signature validation, allowing users to delegate token spending authority via signed messages. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit, LibERC20Permit} from "@compose/contracts/src/modules/erc20/permit/LibERC20Permit.sol"; +import {IERC20PermitMod} from "@compose/contracts/src/modules/erc20/permit/ERC20PermitMod.sol"; + +contract MyERC20Facet { + using LibERC20Permit for IERC20Permit; + IERC20PermitMod private immutable _erc20PermitMod; + + constructor(address _erc20PermitModAddress) { + _erc20PermitMod = IERC20PermitMod(_erc20PermitModAddress); + } + + /** + * @notice Approves an amount of tokens to a spender using a permit signature. + * @param _owner The owner of the tokens. + * @param _spender The address to approve. + * @param _value The amount of tokens to approve. + * @param _deadline The permit deadline. + * @param _v The v component of the signature. + * @param _r The r component of the signature. + * @param _s The s component of the signature. + */ + function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { + // The permit function in the module emits the Approval event. + _erc20PermitMod.permit(_owner, _spender, _value, _deadline, _v, _r, _s); + } + + /** + * @notice Get the domain separator. + * @return The domain separator. + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _erc20PermitMod.DOMAIN_SEPARATOR(); + } +} +`} + + +## Best Practices + + +- Ensure the `permit` function is only callable by authorized entities if underlying token logic requires it. The module itself is permissionless regarding signature validation. +- Always verify the `_deadline` to prevent stale permits from being used. +- If extending ERC-20 functionality, ensure the `Approval` event is correctly emitted by the calling facet when `ERC20PermitMod.permit` is called. + + +## Integration Notes + + +The ERC20PermitMod interacts with the diamond's storage pattern to access and potentially update permit-related state. Facets using this module should ensure they correctly initialize the module and call its functions. The `permit` function in this module emits an `Approval` event, which is expected to be handled by the calling facet or the diamond's event aggregation mechanism. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx new file mode 100644 index 00000000..311231b7 --- /dev/null +++ b/website/docs/contracts/modules/ERC6909Mod.mdx @@ -0,0 +1,532 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic. + + + +- Implements essential ERC-6909 multi-token functionalities: mint, burn, transfer, approve, and setOperator. +- Utilizes a standardized storage slot for efficient access and compatibility with diamond upgrades. +- Supports operator functionality, allowing designated addresses to transfer tokens on behalf of others without requiring explicit approval. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +ERC6909Mod provides the core logic and storage for implementing a minimal ERC-6909 multi-token standard within a Compose diamond. This module enables efficient management of multiple token types, including minting, burning, transfers, and approvals, all managed through a standardized storage pattern compatible with diamond upgrades. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Storage, ERC6909Mod} from "@compose/modules/erc6909/ERC6909.sol"; + +contract MyERC6909Facet { + IERC6909Storage private _storage; + + constructor(address diamondProxy) { + _storage = IERC6909Storage(diamondProxy); + } + + /** + * @notice Mints tokens for a specific ID. + * @param _id The token ID to mint. + * @param _amount The amount to mint. + * @param _to The recipient address. + */ + function mintToken(uint256 _id, uint256 _amount, address _to) external { + ERC6909Mod.mint(_storage, _id, _amount, _to); + } + + /** + * @notice Transfers tokens between addresses. + * @param _id The token ID to transfer. + * @param _amount The amount to transfer. + * @param _from The sender address. + * @param _to The recipient address. + */ + function transferToken(uint256 _id, uint256 _amount, address _from, address _to) external { + ERC6909Mod.transfer(_storage, _id, _amount, _from, _to); + } +}`} + + +## Best Practices + + +- Ensure the `IERC6909Storage` interface is correctly cast to the diamond proxy address when interacting with the module. +- Handle potential errors from `transfer` and `burn` functions, which may revert due to insufficient balance or invalid operations. +- Be mindful of operator roles when implementing `transfer`; operators bypass allowance checks. + + +## Integration Notes + + +ERC6909Mod relies on the `IERC6909Storage` interface to access its internal state, which is managed in a dedicated storage slot within the diamond proxy. Facets integrating this module must correctly pass the diamond proxy address to the module functions, which will then interact with the shared storage. The `getStorage` function can be used by facets to obtain a direct pointer to the ERC6909 storage struct, allowing for read operations without direct module calls. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..4ee3c69f --- /dev/null +++ b/website/docs/contracts/modules/ERC721EnumerableMod.mdx @@ -0,0 +1,362 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. + + + +- Manages internal state for enumerable ERC-721 tokens, including tracking token ownership and supply. +- Provides atomic operations for minting, transferring, and burning tokens, ensuring consistency across enumeration lists. +- Designed for integration within Compose diamond facets, adhering to the diamond storage pattern. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721EnumerableMod provides essential internal logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures that minted, transferred, and burned tokens are correctly tracked in enumeration lists, maintaining data integrity for token ownership and supply. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "./interfaces/IERC721EnumerableMod.sol"; + +contract MyERC721Facet { + // Assume ERC721EnumerableMod is deployed and its address is known + IERC721EnumerableMod internal immutable erc721EnumerableMod; + + constructor(address _erc721EnumerableModAddress) { + erc721EnumerableMod = IERC721EnumerableMod(_erc721EnumerableModAddress); + } + + /** + * @notice Mints a new ERC-721 token. + * @param _to The address to mint the token to. + * @param _tokenId The ID of the token to mint. + */ + function mintToken(address _to, uint256 _tokenId) external { + // Call the internal mint logic provided by the module + erc721EnumerableMod.mint(_to, _tokenId); + } + + /** + * @notice Transfers an existing ERC-721 token. + * @param _from The address to transfer the token from. + * @param _to The address to transfer the token to. + * @param _tokenId The ID of the token to transfer. + */ + function transferToken(address _from, address _to, uint256 _tokenId) external { + // Call the internal transfer logic provided by the module + erc721EnumerableMod.transferFrom(_from, _to, _tokenId); + } + + /** + * @notice Burns an ERC-721 token. + * @param _tokenId The ID of the token to burn. + */ + function burnToken(uint256 _tokenId) external { + // Call the internal burn logic provided by the module + erc721EnumerableMod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721EnumerableMod` is correctly initialized and its address is accessible to facets that require its functionality. +- Always validate input parameters for token IDs and recipient addresses before calling module functions to prevent unexpected state changes or reverts. +- Implement robust access control within your facets to ensure only authorized entities can initiate mint, transfer, or burn operations. + + +## Integration Notes + + +The `ERC721EnumerableMod` interacts with a predefined ERC-721 enumerable storage struct within the diamond's storage. The `getStorage` function allows facets to access this struct directly using inline assembly, ensuring efficient retrieval. Changes made by `mint`, `transferFrom`, and `burn` directly update this shared storage, making them immediately visible to all facets interacting with the ERC-721 enumerable state. Facets should not attempt to replicate this storage management logic. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx new file mode 100644 index 00000000..a001a521 --- /dev/null +++ b/website/docs/contracts/modules/ERC721Mod.mdx @@ -0,0 +1,359 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. + + + +- Provides core ERC-721 operations: mint, burn, transfer. +- Integrates with diamond storage using a predefined storage slot for ERC-721 state. +- Reverts on invalid operations such as minting an existing token or burning a non-existent token. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod module provides essential internal logic for managing ERC-721 compliant tokens within a Compose diamond. It enables facets to safely mint, transfer, burn, and manage metadata for non-fungible tokens, leveraging the diamond's storage pattern for efficient and composable state management. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod} from "@compose/modules/ERC721Mod.sol"; + +contract MyERC721Facet { + address constant ERC721_STORAGE_SLOT = 0x5f636306d713946618412e127e1079e4c27849b993960d260f01f2e124712e60; // Example slot, replace with actual + + function mintToken(address _to, uint256 _tokenId) external { + IERC721Mod erc721Mod = IERC721Mod(address(this)); // Assuming facet has access to module + erc721Mod.mint(_to, _tokenId); + } + + function transferExistingToken(address _from, address _to, uint256 _tokenId) external { + IERC721Mod erc721Mod = IERC721Mod(address(this)); + erc721Mod.transferFrom(_from, _to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + IERC721Mod erc721Mod = IERC721Mod(address(this)); + erc721Mod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the ERC721Mod contract is correctly initialized and accessible to facets that require ERC-721 functionality. +- Implement robust access control within your facets to restrict who can call mint, burn, and transfer functions if necessary. +- Handle potential reverts from module functions (e.g., token not existing during burn, zero address during mint) gracefully within your facet logic. + + +## Integration Notes + + +The ERC721Mod interacts with diamond storage at a predefined slot to manage its internal ERC-721 state. Facets must be aware of this storage layout and the specific slot used to ensure correct initialization and data access. The `getStorage` function can be used by facets to retrieve the ERC-721 storage struct directly, allowing for read operations without calling specific management functions. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx new file mode 100644 index 00000000..21d09e98 --- /dev/null +++ b/website/docs/contracts/modules/NonReentrancyMod.mdx @@ -0,0 +1,152 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. + + + +- Prevents reentrant function calls by maintaining a simple lock state. +- Designed for seamless integration into Compose diamond facets using the `using for` directive. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides essential utilities to prevent reentrancy attacks within your diamond facets. By integrating this module, you can ensure that sensitive operations are executed atomically, safeguarding your contract's state integrity. + +--- + +## Storage + +--- +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose-protocol/contracts/src/modules/non-reentrancy/LibNonReentrancy.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 private _lock; + + /** + * @notice Example function protected by non-reentrancy. + */ + function protectedAction() external { + // Acquire the lock before executing sensitive logic. + _lock.enter(); + + // ... perform sensitive operations ... + + // Release the lock after operations are complete. + _lock.exit(); + } + + /** + * @notice Another function demonstrating reentrancy protection. + */ + function anotherAction() external { + // Enter the non-reentrancy lock. + _lock.enter(); + + // ... perform operations ... + + // Exit the non-reentrancy lock. + _lock.exit(); + } +}`} + + +## Best Practices + + +- Always pair `enter()` with `exit()` to ensure the lock is released, even in the event of an internal revert. +- Use `_lock.enter()` immediately upon entering a function and `_lock.exit()` just before returning or reverting to maximize protection. +- Integrate this module into facets that handle critical state changes or asset transfers to prevent recursive calls. + + +## Integration Notes + + +The NonReentrancyMod relies on a single `uint256` storage variable to track the reentrancy lock. Facets using this module should declare a `uint256` variable (e.g., `_lock`) and associate it with the `LibNonReentrancy` library using `using LibNonReentrancy for uint256;`. The `enter()` function increments this variable, and `exit()` decrements it. It is crucial that the storage slot for this lock variable is not shared or modified by other facets in a way that could compromise the lock's integrity. The order of operations within a facet is critical: `enter()` must be called before any potentially reentrant calls, and `exit()` must be called after all sensitive operations are completed. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx new file mode 100644 index 00000000..c063506d --- /dev/null +++ b/website/docs/contracts/modules/OwnerMod.mdx @@ -0,0 +1,258 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. + + + +- Provides ERC-173 compliant owner management functions (`owner`, `transferOwnership`, `setContractOwner`). +- Includes a utility function `requireOwner()` for easy access control enforcement. +- Defines a clear storage layout for owner tracking, compatible with the Compose diamond storage pattern. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +OwnerMod provides essential ERC-173 contract ownership management for Compose diamonds. It defines the storage layout and functions required to track and enforce contract ownership, ensuring that critical administrative actions can only be performed by the designated owner. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; +import {OwnerModStorage} from "@compose/modules/owner/OwnerModStorage.sol"; + +contract MyOwnerFacet { + uint256 constant OWNER_STORAGE_SLOT = 1; // Example slot + + function owner() external view returns (address) { + return IOwnerMod(address(this)).owner(); + } + + function transferContractOwnership(address _newOwner) external { + IOwnerMod(address(this)).transferOwnership(_newOwner); + } + + function setOwner(address _newOwner) external { + // Assuming OwnerMod is correctly initialized with the owner + // and this facet has access to the storage. + IOwnerMod(address(this)).setContractOwner(_newOwner); + } + + // Example of protecting a function with owner-only access + function sensitiveAdminAction() external { + IOwnerMod(address(this)).requireOwner(); + // Perform sensitive action + } +}`} + + +## Best Practices + + +- Always use `requireOwner()` before executing any function that modifies critical contract state or performs administrative tasks. +- When transferring ownership, consider the implications of setting the new owner to `address(0)` as this renounces ownership permanently. +- Ensure the `OwnerModStorage` struct is correctly laid out in your diamond's storage, respecting the defined `STORAGE_POSITION`. + + +## Integration Notes + + +The OwnerMod integrates with the diamond's storage by defining a specific storage slot for its `OwnerModStorage` struct. Facets interacting with owner functionalities must use the `IOwnerMod` interface to call the provided functions. The `getStorage` function within OwnerMod (though not directly callable by external facets) uses inline assembly to access this specific storage slot. Changes to the owner are immediately reflected and enforced by subsequent calls to `requireOwner()`. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..ece21660 --- /dev/null +++ b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx @@ -0,0 +1,307 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. + + + +- Two-step ownership transfer for enhanced security. +- `renounceOwnership` function to set owner to `address(0)`. +- `requireOwner` modifier-like functionality for access control. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerTwoStepsMod provides a secure, two-step ownership transfer mechanism for your diamond. This pattern prevents accidental ownership loss by requiring explicit acceptance from the new owner, enhancing contract safety and auditability. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +Storage position: `OWNER_STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {OwnerTwoStepsMod} from "@compose/modules/ownership/OwnerTwoStepsMod.sol"; + +contract MyFacet { + OwnerTwoStepsMod private ownerMod; + + constructor(address ownerTwoStepsModAddress) { + ownerMod = OwnerTwoStepsMod(ownerTwoStepsModAddress); + } + + function transferContractOwnership(address _newOwner) external { + ownerMod.transferOwnership(_newOwner); + } + + function acceptContractOwnership() external { + ownerMod.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return ownerMod.owner(); + } + + function getPendingContractOwner() external view returns (address) { + return ownerMod.pendingOwner(); + } + + function protectAdminFunction() external { + ownerMod.requireOwner(); + // ... admin logic ... + } +}`} + + +## Best Practices + + +- Always use `transferOwnership` followed by `acceptOwnership` for ownership changes to prevent accidental lockouts. +- Ensure the `OwnerTwoStepsMod` contract is deployed and accessible by your facets. +- Consider gas implications for users when designing ownership transfer workflows. + + +## Integration Notes + + +The OwnerTwoStepsMod relies on specific storage slots for its `Owner` and `PendingOwner` state variables, defined by `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` respectively. Facets interacting with this module should be aware that these storage slots are managed by the module. Changes to these storage slots outside of the module's functions will lead to unpredictable behavior. The module provides `getOwnerStorage` and `getPendingOwnerStorage` for direct access if necessary, but direct manipulation is discouraged. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx new file mode 100644 index 00000000..1d562d25 --- /dev/null +++ b/website/docs/contracts/modules/RoyaltyMod.mdx @@ -0,0 +1,358 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic." +gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic. + + + +- Implements ERC-2981 standard for royalty payments. +- Supports both default and token-specific royalty configurations. +- Provides functions to set, reset, and query royalty information efficiently. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The RoyaltyMod module provides an implementation of the ERC-2981 royalty standard, enabling diamonds to manage and query royalty information for tokens. It supports both default royalties applicable to all tokens and token-specific overrides, ensuring compliance with royalty distribution requirements. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. +--- +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "../interfaces/IRoyaltyMod.sol"; +import {IDiamondStorage} from "../interfaces/IDiamondStorage.sol"; + +contract RoyaltyFacet { + // Assume IDiamondStorage is available and initialized + IDiamondStorage internal _diamondStorage; + + constructor(address diamondAddress) { + _diamondStorage = IDiamondStorage(diamondAddress); + } + + /** + * @notice Sets royalty information for a specific token. + * @param _tokenId The ID of the token. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee in basis points. + */ + function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + // Access the RoyaltyMod internal functions via the diamond storage interface + IRoyaltyMod(address(_diamondStorage.getRoyaltyMod())).setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Queries royalty information for a given token and sale price. + * @param _tokenId The ID of the token. + * @param _salePrice The sale price of the token. + * @return _receiver The address to receive royalties. + * @return _feeBasisPoints The royalty fee in basis points. + */ + function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address _receiver, uint256 _feeBasisPoints) { + // Access the RoyaltyMod internal functions via the diamond storage interface + return IRoyaltyMod(address(_diamondStorage.getRoyaltyMod())).royaltyInfo(_tokenId, _salePrice); + } +}`} + + +## Best Practices + + +- Ensure the `_receiver` address is validated for safety and intent before setting royalties. +- Use `deleteDefaultRoyalty` or `resetTokenRoyalty` to clear royalty information when no longer applicable, adhering to gas efficiency principles. +- Be aware that `royaltyInfo` queries will fallback to default royalties if no token-specific royalty is set. + + +## Integration Notes + + +The RoyaltyMod is designed to be integrated via a diamond proxy. Facets interacting with royalty logic should call the `IRoyaltyMod` interface, which is expected to be accessible through the diamond's storage mechanism (e.g., `_diamondStorage.getRoyaltyMod()`). The `royaltyInfo` function queries token-specific royalties first and falls back to default royalties if none are found for the given token. Storage for default royalties is managed in a predefined slot, while token-specific royalties are stored in a mapping accessible via the module. + + +
+ +
+ + From dd429951e2bd1c7695c019a56716550ba0ab11de Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 19:58:52 -0500 Subject: [PATCH 35/68] fix storage location rendering --- .../generate-docs-utils/pr-body-generator.js | 2 +- .../generate-docs-utils/templates/helpers.js | 18 ++++++++++++++++++ .../templates/pages/contract.mdx.template | 2 +- .../templates/template-engine-handlebars.js | 6 ++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/scripts/generate-docs-utils/pr-body-generator.js b/.github/scripts/generate-docs-utils/pr-body-generator.js index a85fbcdc..c37678cb 100644 --- a/.github/scripts/generate-docs-utils/pr-body-generator.js +++ b/.github/scripts/generate-docs-utils/pr-body-generator.js @@ -60,7 +60,7 @@ function generatePRBody(summary) { body += '- [ ] Ensure consistency with existing docs\n\n'; body += '---\n'; - body += '** 🚨 This PR was automatically generated. Please ALWAYS review before merging **\n'; + body += '🚨 **This PR was automatically generated. Please ALWAYS review before merging.**\n'; body += `Generated on: ${new Date().toISOString()}\n`; return body; diff --git a/.github/scripts/generate-docs-utils/templates/helpers.js b/.github/scripts/generate-docs-utils/templates/helpers.js index dd8a17fd..ad66992f 100644 --- a/.github/scripts/generate-docs-utils/templates/helpers.js +++ b/.github/scripts/generate-docs-utils/templates/helpers.js @@ -105,6 +105,23 @@ function escapeHtml(str) { .replace(/'/g, '''); } +/** + * Escape string for use in JavaScript/JSX object literal values + * Escapes quotes and backslashes for JavaScript strings (not HTML entities) + * @param {string} str - String to escape + * @returns {string} Escaped string safe for JavaScript string literals + */ +function escapeJsString(str) { + if (!str) return ''; + return String(str) + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/"/g, '\\"') // Escape double quotes + .replace(/'/g, "\\'") // Escape single quotes + .replace(/\n/g, '\\n') // Escape newlines + .replace(/\r/g, '\\r') // Escape carriage returns + .replace(/\t/g, '\\t'); // Escape tabs +} + module.exports = { escapeYaml, escapeJsx, @@ -113,5 +130,6 @@ module.exports = { toJsxExpression, escapeMarkdownTable, escapeHtml, + escapeJsString, }; diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index b5ba3e84..cbd6f4cd 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -82,7 +82,7 @@ This module provides internal functions for use in your custom facets. Import it { name: "{{name}}", type: "{{#if type}}{{type}}{{else}}constant{{/if}}", - description: "{{#if description}}{{escapeJsx description}}{{/if}}{{#if value}} (Value: `{{value}}`){{/if}}" + description: "{{#if description}}{{escapeJsx description}}{{/if}}{{#if value}} (Value: `{{escapeJsString value}}`){{/if}}" }{{#unless @last}},{{/unless}} {{/each}} ]} diff --git a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js index bb123693..b852dd91 100644 --- a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js +++ b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js @@ -25,6 +25,12 @@ function registerHelpers() { Handlebars.registerHelper('escapeJsx', helpers.escapeJsx); Handlebars.registerHelper('sanitizeMdx', helpers.sanitizeMdx); Handlebars.registerHelper('escapeMarkdownTable', helpers.escapeMarkdownTable); + // Helper to escape value for JavaScript strings in JSX object literals + Handlebars.registerHelper('escapeJsString', function(value) { + if (!value) return ''; + const escaped = helpers.escapeJsString(value); + return new Handlebars.SafeString(escaped); + }); // Helper to emit a JSX style literal: returns a string like {{display: "flex", gap: "1rem"}} Handlebars.registerHelper('styleLiteral', function(styles) { From fa8f8e1bb6dcbb86c8649ceb002906cc83d6cd1d Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 20:58:48 -0500 Subject: [PATCH 36/68] remove storage info --- .../generate-docs-utils/templates/pages/contract.mdx.template | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index cbd6f4cd..b414aa2f 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -69,10 +69,6 @@ This module provides internal functions for use in your custom facets. Import it {{#if hasStorage}} -{{#if storageInfo}} -{{{sanitizeMdx storageInfo}}} -{{/if}} ---- {{#if hasStateVariables}} ### State Variables From 8fcbb812c7f4f59338db6b04ce17631a729a30f9 Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 20:59:51 -0500 Subject: [PATCH 37/68] remove old pages --- .../contracts/facets/AccessControlFacet.mdx | 547 ------------- .../facets/AccessControlPausableFacet.mdx | 386 --------- .../facets/AccessControlTemporalFacet.mdx | 459 ----------- .../docs/contracts/facets/DiamondCutFacet.mdx | 445 ----------- .../contracts/facets/DiamondLoupeFacet.mdx | 255 ------ .../docs/contracts/facets/ERC1155Facet.mdx | 682 ---------------- .../contracts/facets/ERC20BridgeableFacet.mdx | 432 ---------- .../docs/contracts/facets/ERC20BurnFacet.mdx | 260 ------ website/docs/contracts/facets/ERC20Facet.mdx | 571 ------------- .../contracts/facets/ERC20PermitFacet.mdx | 339 -------- .../docs/contracts/facets/ERC6909Facet.mdx | 531 ------------- .../docs/contracts/facets/ERC721BurnFacet.mdx | 215 ----- .../facets/ERC721EnumerableBurnFacet.mdx | 233 ------ .../facets/ERC721EnumerableFacet.mdx | 749 ------------------ website/docs/contracts/facets/ERC721Facet.mdx | 669 ---------------- .../docs/contracts/facets/ExampleDiamond.mdx | 150 ---- website/docs/contracts/facets/OwnerFacet.mdx | 213 ----- .../contracts/facets/OwnerTwoStepsFacet.mdx | 292 ------- .../docs/contracts/facets/RoyaltyFacet.mdx | 199 ----- .../contracts/modules/AccessControlMod.mdx | 451 ----------- .../modules/AccessControlPausableMod.mdx | 405 ---------- .../modules/AccessControlTemporalMod.mdx | 504 ------------ .../docs/contracts/modules/DiamondCutMod.mdx | 379 --------- website/docs/contracts/modules/DiamondMod.mdx | 237 ------ website/docs/contracts/modules/ERC1155Mod.mdx | 616 -------------- website/docs/contracts/modules/ERC165Mod.mdx | 162 ---- .../contracts/modules/ERC20BridgeableMod.mdx | 438 ---------- website/docs/contracts/modules/ERC20Mod.mdx | 426 ---------- .../docs/contracts/modules/ERC20PermitMod.mdx | 296 ------- website/docs/contracts/modules/ERC6909Mod.mdx | 532 ------------- .../contracts/modules/ERC721EnumerableMod.mdx | 362 --------- website/docs/contracts/modules/ERC721Mod.mdx | 359 --------- .../contracts/modules/NonReentrancyMod.mdx | 152 ---- website/docs/contracts/modules/OwnerMod.mdx | 258 ------ .../contracts/modules/OwnerTwoStepsMod.mdx | 307 ------- website/docs/contracts/modules/RoyaltyMod.mdx | 358 --------- 36 files changed, 13869 deletions(-) delete mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721Facet.mdx delete mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx delete mode 100644 website/docs/contracts/facets/OwnerFacet.mdx delete mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx delete mode 100644 website/docs/contracts/modules/AccessControlMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondMod.mdx delete mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC165Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC20Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx delete mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC721Mod.mdx delete mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx deleted file mode 100644 index ffe9929f..00000000 --- a/website/docs/contracts/facets/AccessControlFacet.mdx +++ /dev/null @@ -1,547 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Role-based access control (RBAC) facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control (RBAC) facet for Compose diamonds - - - -- Standardized RBAC implementation compatible with EIP-2678. -- Batch operations for granting and revoking roles to improve gas efficiency. -- Supports role hierarchy through role administration. - - -## Overview - -The AccessControlFacet implements a robust role-based access control (RBAC) system for Compose diamonds. It enables granular permission management by defining roles and assigning them to accounts, ensuring that only authorized entities can perform sensitive operations. This facet acts as a central authority for enforcing access policies across various diamond functionalities. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose/diamond-loupe/facets/DiamondLoupeFacet.sol"; -import {AccessControlFacet} from "@compose/access-control/facets/AccessControlFacet.sol"; - -contract Deployer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function grantAdminRoleToDeployer(address deployerAddress) external { - AccessControlFacet accessControl = AccessControlFacet(diamondAddress); - // Assume DEFAULT_ADMIN_ROLE is defined and accessible - // bytes32 DEFAULT_ADMIN_ROLE = accessControl.DEFAULT_ADMIN_ROLE(); // Hypothetical, actual constant needed - bytes32 adminRole = keccak256("DEFAULT_ADMIN_ROLE"); // Example role - accessControl.grantRole(adminRole, deployerAddress); - } - - function checkDeployerRole(address deployerAddress) external view returns (bool) { - AccessControlFacet accessControl = AccessControlFacet(diamondAddress); - bytes32 adminRole = keccak256("DEFAULT_ADMIN_ROLE"); // Example role - return accessControl.hasRole(adminRole, deployerAddress); - } -}`} - - -## Best Practices - - -- Initialize roles and grant administrative privileges during the diamond deployment process. -- Use `requireRole` within other facets to protect sensitive functions, ensuring calls are made by authorized roles. -- Manage role-to-role admin relationships carefully using `setRoleAdmin` to maintain a clear hierarchy. - - -## Security Considerations - - -Access to `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch` is restricted to the admin of the role being modified. `renounceRole` can only be called by the sender. Input validation is handled internally by the facet. Reentrancy is not a concern as these functions do not make external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx deleted file mode 100644 index 333c6ce4..00000000 --- a/website/docs/contracts/facets/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,386 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Role-based access control with pause functionality for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control with pause functionality for Compose diamonds - - - -- Role-specific pausing: Temporarily disable specific roles without affecting others. -- Admin-controlled operations: Only the designated admin for a role can pause or unpause it. -- Composable access control: Integrates seamlessly with Compose diamond's facet architecture. - - -## Overview - -The AccessControlPausableFacet integrates role-based access control with pause functionality into a Compose diamond. It allows for granular pausing and unpausing of specific roles, ensuring that sensitive operations can be temporarily halted by authorized administrators. This facet provides essential mechanisms for managing privileged actions within the diamond's ecosystem. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose-protocol/diamond/contracts/interfaces/IDiamondCut.sol"; -import {AccessControlPausableFacet} from "@compose-protocol/diamond/facets/AccessControl/AccessControlPausableFacet.sol"; - -contract DeployDiamond { - address constant DIAMOND_CUT_FACET_ADDRESS = address(0x1); // Replace with actual address - address constant ACCESS_CONTROL_PAUSABLE_FACET_ADDRESS = address(0x2); // Replace with actual address - - function deploy() public { - // Assume diamondCutFacet is already deployed and initialized - IDiamondCut diamondCutFacet = IDiamondCut(DIAMOND_CUT_FACET_ADDRESS); - - // Deploy AccessControlPausableFacet and get its deployment address - // In a real scenario, you would use a factory or deploy directly - address accessControlPausableFacet = ACCESS_CONTROL_PAUSABLE_FACET_ADDRESS; // Placeholder - - // Define the functions to be added by this facet - Facet[] memory facetsToAdd = new Facet[](1); - facetsToAdd[0] = Facet({ - facetAddress: accessControlPausableFacet, - facetCuts: new FacetCut[]( - // Add all functions from AccessControlPausableFacet - // Example: addAccessControlPausableFacetFunctions(accessControlPausableFacet) - ) - }); - - // Note: Initialization logic for AccessControlPausableFacet (e.g., setting admin) would happen here - // or as a separate call after deployment. - - // diamondCutFacet.diamondCut(facetsToAdd, address(0), ""); - } - - // Helper to get function selectors (example, actual implementation depends on facet contract) - // function addAccessControlPausableFacetFunctions(address facetAddress) internal pure returns (FacetCut[] memory) { - // // ... logic to get selectors for getAccessControlStorage, getStorage, etc. ... - // } -}`} - - -## Best Practices - - -- Initialize the facet with the correct admin role to control pausing operations. -- Use `requireRoleNotPaused` proactively before calling sensitive functions that depend on role availability. -- Store the facet's storage structs in your diamond's storage layout, ensuring correct slotting and no overwrites. - - -## Security Considerations - - -Ensure that the `admin` role is appropriately managed and secured, as this role has the authority to pause and unpause critical functions. The `AccessControlUnauthorizedAccount` error prevents unauthorized callers from pausing/unpausing roles. The `AccessControlRolePaused` error guards against operations on paused roles. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx deleted file mode 100644 index e20abb52..00000000 --- a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,459 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Time-limited role-based access control for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Time-limited role-based access control for Compose diamonds - - - -- Time-limited role assignments that automatically expire. -- Explicit checks for role validity, considering expiry. -- Granular control over role lifecycles via admin-controlled granting and revoking. - - -## Overview - -The AccessControlTemporalFacet provides time-limited role-based access control for Compose diamonds. It allows granting roles that automatically expire and checking for role validity, enhancing dynamic permission management within the diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; -import {AccessControlTemporalFacet} from "@compose/diamond-contracts/contracts/facets/AccessControlTemporalFacet.sol"; - -contract DeployAccessControlTemporal { - function deploy() public { - // Assume diamondProxy and admin are already deployed and initialized - address diamondProxy = address(0x...); // Address of your diamond proxy - address admin = address(0x...); // Address of the role admin - - // Deploy the facet - AccessControlTemporalFacet temporalFacet = new AccessControlTemporalFacet(); - address temporalFacetAddress = address(temporalFacet); - - // Prepare facet cut data - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: temporalFacetAddress, - action: IDiamondCut.FacetCutAction.ADD, - functionSelectors: AccessControlTemporalFacet.getSelectors() - }); - - // Add the facet to the diamond (requires admin role) - // IDiamondCut(diamondProxy).diamondCut(cut, address(0x0), ""); - - // Example of granting a role with expiry (requires role admin permission) - // uint64 expiryTimestamp = uint64(block.timestamp) + 3600; // 1 hour from now - // AccessControlTemporalFacet(diamondProxy).grantRoleWithExpiry(bytes32("ROLE_NAME"), address(0x1), expiryTimestamp); - - // Example of checking role validity - // bool isValid = AccessControlTemporalFacet(diamondProxy).isRoleExpired(bytes32("ROLE_NAME"), address(0x1)); - } -}`} - - -## Best Practices - - -- Initialize the diamond with the AccessControlFacet before adding this temporal facet to manage roles effectively. -- Ensure the role granting functions are called by the designated role admin to maintain security. -- Store role expiry timestamps appropriately to avoid accidental role expiration if the role is intended to be permanent. - - -## Security Considerations - - -Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role manipulation. The `requireValidRole` function correctly reverts if a role has expired or is not held, preventing unauthorized actions. Ensure proper management of role admin permissions to prevent privilege escalation. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx deleted file mode 100644 index f59de0ee..00000000 --- a/website/docs/contracts/facets/DiamondCutFacet.mdx +++ /dev/null @@ -1,445 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Diamond upgrade (cut) facet for ERC-2535 diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond upgrade (cut) facet for ERC-2535 diamonds - - - -- Supports adding new facets and their functions to the diamond. -- Enables replacement of existing facet logic by updating function selectors. -- Allows for the removal of facets and their associated functions from the diamond. -- Provides access to the diamond's owner storage and the diamond's internal storage layout. - - -## Overview - -The DiamondCutFacet provides the core upgrade mechanism for ERC-2535 compliant diamonds. It allows authorized addresses to add, replace, or remove functions (facets) from the diamond proxy, enabling dynamic modification of the diamond's capabilities. This facet is crucial for managing the diamond's evolving logic and feature set. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - ---- -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutFacet} from "@compose/diamond/facets/DiamondCutFacet.sol"; -import {IDiamondCut} from "@compose/diamond/interfaces/IDiamondCut.sol"; - -contract DeployDiamond { - address constant DIAMOND_ADDRESS = address(0x1234567890abcdef); // Replace with your diamond address - - function upgradeDiamond() public { - DiamondCutFacet diamondCutFacet = DiamondCutFacet(DIAMOND_ADDRESS); - - // Example: Add a new facet - // Assume NewFacetABI is the ABI of the new facet contract - // Assume newFacetAddress is the deployed address of the new facet - // FunctionSelectors for the new facet functions are needed here - // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - // cut[0] = IDiamondCut.FacetCut({ - // facetAddress: newFacetAddress, - // action: IDiamondCut.FacetCutAction.ADD, - // functionSelectors: newFacetFunctionSelectors - // }); - // diamondCutFacet.diamondCut(cut, address(0), ""); - } - - function replaceFacetLogic() public { - // Example: Replace an existing facet - // Assume ExistingFacetABI and existingFacetAddress are known - // Assume selectorsToReplace are the selectors of functions to be replaced - // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - // cut[0] = IDiamondCut.FacetCut({ - // facetAddress: existingFacetAddress, - // action: IDiamondCut.FacetCutAction.REPLACE, - // functionSelectors: selectorsToReplace - // }); - // diamondCutFacet.diamondCut(cut, address(0), ""); - } - - function removeFacet() public { - // Example: Remove a facet - // Assume selectorsToRemove are the selectors of functions to be removed - // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - // cut[0] = IDiamondCut.FacetCut({ - // facetAddress: address(0), // Address is ignored when removing - // action: IDiamondCut.FacetCutAction.REMOVE, - // functionSelectors: selectorsToRemove - // }); - // diamondCutFacet.diamondCut(cut, address(0), ""); - } - - // Function to retrieve owner storage, requires knowledge of STORAGE_POSITION - function getOwnerStoragePointer() public view returns (address) { - return diamondCutFacet.getOwnerStorage(); - } -}`} - - -## Best Practices - - -- Ensure only authorized addresses can call `diamondCut` and its related functions. Access control should be managed externally or via another facet. -- Carefully construct `FacetCut` arrays to avoid unintended function removals or replacements. Audit selector lists before execution. -- Use `diamondCut` with caution, especially when replacing functions. Ensure new facet logic is compatible and thoroughly tested. - - -## Security Considerations - - -The `diamondCut` function is a critical administrative function. Unauthorized access can lead to the complete compromise of the diamond's functionality. Implement robust access control mechanisms to restrict its usage. Reentrancy is not directly applicable to `diamondCut` itself, but any `delegatecall` executed via `diamondCut` must be carefully audited for reentrancy vulnerabilities. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx deleted file mode 100644 index 11cb7d4a..00000000 --- a/website/docs/contracts/facets/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,255 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "The functions in DiamondLoupeFacet MUST be added to a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -The functions in DiamondLoupeFacet MUST be added to a diamond. - - - -- Provides functions to retrieve all facets and their associated function selectors within the diamond. -- Enables querying the specific facet address responsible for a given function selector. -- Offers an efficient way to list all unique facet addresses deployed in the diamond. -- Optimized for performance, using memory-based hash maps to reduce gas costs for complex diamonds. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query the diamond's internal structure, specifically identifying which facets are deployed, the functions they support, and the addresses they are mapped to. This facet is crucial for understanding and interacting with the diamond's composable architecture. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose/diamond-contracts/facets/DiamondLoupeFacet.sol"; -import {IDiamondLoupeFacet} from "@compose/diamond-contracts/facets/DiamondLoupeFacet.sol"; - -contract DiamondLoupeConsumer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getDiamondFacets() external view returns (IDiamondLoupeFacet.Facet[] memory) { - DiamondLoupeFacet loupe = DiamondLoupeFacet(diamondAddress); - return loupe.facets(); - } - - function getFacetAddress(bytes4 _selector) external view returns (address) { - DiamondLoupeFacet loupe = DiamondLoupeFacet(diamondAddress); - return loupe.facetAddress(_selector); - } -}`} - - -## Best Practices - - -- Integrate `DiamondLoupeFacet` into your diamond to enable runtime inspection of its facets and function mappings. -- Use the provided functions to dynamically discover facet addresses and their supported selectors, facilitating interaction with various diamond functionalities. -- Ensure that `DiamondLoupeFacet` is initialized with the correct diamond storage pointers during deployment. - - -## Security Considerations - - -This facet is primarily for introspection and does not modify state. Ensure that the diamond's upgrade mechanism correctly updates the facet mappings. Access control should be managed at the facet level, not within DiamondLoupeFacet itself, as it exposes internal diamond structure. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx deleted file mode 100644 index a43f89e1..00000000 --- a/website/docs/contracts/facets/ERC1155Facet.mdx +++ /dev/null @@ -1,682 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "ERC-1155 multi-token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-1155 multi-token facet for Compose diamonds - - - -- Supports both single token transfers and batched transfers for efficiency. -- Provides methods to query token ownership (`balanceOf`, `balanceOfBatch`) and operator approvals (`isApprovedForAll`). -- Implements URI resolution for tokens, allowing for dynamic metadata linking. -- Integrates seamlessly into the Compose diamond pattern, allowing for modular extension and upgradeability. - - -## Overview - -The ERC1155Facet provides a robust implementation for managing and transferring ERC-1155 multi-tokens within a Compose diamond. It handles token balances, approvals, and URI resolution, enabling a wide range of fungible and non-fungible token use cases. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut, IERC1155Facet} from "@compose/diamond-contracts/contracts/interfaces/IDiamond.sol"; -import {ERC1155Facet} from "@compose/diamond-contracts/contracts/facets/ERC1155Facet.sol"; - -contract DeployERC1155Diamond { - address diamondAddress; - - function deploy() public { - // Assume diamondAddress is already deployed and initialized - // ... deploy diamond proxy and set initial facets ... - - // Deploy the ERC1155Facet - ERC1155Facet erc1155Facet = new ERC1155Facet(); - - // Prepare diamond cut for adding the ERC1155Facet - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: address(erc1155Facet), - action: IDiamondCut.Action.ADD, - selectors: IDiamondCut.getSelectors(erc1155Facet) - }); - - // Call diamond loupe to cut the facet - // Assume diamondLoupe is the contract implementing IDiamondCut - // diamondLoupe.diamondCut(cut, address(0), ""); - - // For demonstration, directly call a function if diamondAddress is known - IERC1155Facet(diamondAddress).setApprovalForAll(msg.sender, true); - uint256 tokenId = 1; - uint256 amount = 100; - address owner = address(this); - address recipient = address(1); - // IERC1155Facet(diamondAddress).safeTransferFrom(owner, recipient, tokenId, amount, ""); - } -}`} - - -## Best Practices - - -- Initialize the ERC1155 storage correctly during diamond deployment to set the base URI and any initial token URIs. -- Implement access control mechanisms within your diamond's logic contract or separate facets to govern who can call administrative functions like `setApprovalForAll` or mint/burn operations (if implemented in custom facets). -- Ensure that any custom facets interacting with ERC1155 storage respect the storage layout and slot definitions of the `ERC1155Facet` to avoid conflicts. - - -## Security Considerations - - -This facet implements standard ERC-1155 functionality. Ensure that functions not exposed by this facet, such as minting or burning, are implemented in separate facets with appropriate access controls to prevent unauthorized token creation or destruction. Reentrancy is not a direct concern for the functions exposed by this facet itself, but downstream interactions with external contracts in `safeTransferFrom` and `safeBatchTransferFrom` should be audited for reentrancy vulnerabilities if the `to` address or `data` parameter leads to external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx deleted file mode 100644 index 855af877..00000000 --- a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,432 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "ERC-20 token bridgeable facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token bridgeable facet for Compose diamonds - - - -- Enables cross-chain token transfers by allowing trusted bridges to mint and burn ERC-20 tokens. -- Integrates with the diamond's access control system to enforce authorization for bridging operations. -- Provides internal utility functions (`getERC20Storage`, `getAccessControlStorage`, `checkTokenBridge`) for interacting with diamond storage and access control. - - -## Overview - -The ERC20BridgeableFacet enables cross-chain minting and burning of ERC-20 tokens within a Compose diamond. It orchestrates token bridging operations by interacting with diamond storage and access control mechanisms to verify trusted bridge addresses. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20BridgeableFacet} from "@compose/contracts/facets/ERC20/ERC20BridgeableFacet.sol"; -import {AccessControlFacet} from "@compose/contracts/facets/AccessControl/AccessControlFacet.sol"; - -contract ERC20BridgeableFacetConsumer { - ERC20BridgeableFacet public erc20BridgeableFacet; - AccessControlFacet public accessControlFacet; - - constructor(address _diamondAddress) { - erc20BridgeableFacet = ERC20BridgeableFacet(_diamondAddress); - accessControlFacet = AccessControlFacet(_diamondAddress); - } - - /** - * @notice Example of minting tokens via the bridge. - * @param _token The ERC20 token address. - * @param _to The recipient address. - * @param _amount The amount to mint. - */ - function mintTokens(address _token, address _to, uint256 _amount) external { - // Assume the caller has the 'trusted-bridge' role. - // In a real scenario, access control would be enforced by the diamond proxy itself - // or by a separate caller with the appropriate role. - erc20BridgeableFacet.crosschainMint(_token, _to, _amount); - } - - /** - * @notice Example of burning tokens via the bridge. - * @param _token The ERC20 token address. - * @param _from The sender address. - * @param _amount The amount to burn. - */ - function burnTokens(address _token, address _from, uint256 _amount) external { - // Assume the caller has the 'trusted-bridge' role. - erc20BridgeableFacet.crosschainBurn(_token, _from, _amount); - } -}`} - - -## Best Practices - - -- Initialize the `ERC20BridgeableFacet` by adding it to the diamond proxy during deployment. -- Ensure the `trusted-bridge` role is correctly assigned to authorized bridge addresses in the `AccessControlFacet`. -- Use the `checkTokenBridge` internal function or rely on the diamond's access control mechanisms to verify bridge authorization before calling `crosschainMint` or `crosschainBurn`. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role. Ensure that only authorized and audited bridge contracts or addresses are granted this role to prevent unauthorized token minting or burning. The `checkTokenBridge` function explicitly verifies the caller's `trusted-bridge` role, mitigating risks from unauthorized callers. Reentrancy is not a direct concern for these mint/burn functions as they do not perform external calls back to untrusted contracts. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx deleted file mode 100644 index d74214f5..00000000 --- a/website/docs/contracts/facets/ERC20BurnFacet.mdx +++ /dev/null @@ -1,260 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "ERC-20 token burn facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token burn facet for Compose diamonds - - - -- Supports burning tokens from the caller's balance (`burn`). -- Supports burning tokens from another account's balance, respecting allowances (`burnFrom`). -- Integrates with the Compose diamond storage pattern for ERC-20 state management. - - -## Overview - -The ERC20BurnFacet provides functionality to burn ERC-20 tokens within a Compose diamond. It allows users to destroy tokens from their own balance or from another account's balance, reducing the total supply. This facet integrates seamlessly with the diamond's storage pattern for managing ERC-20 state. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BurnFacet} from "../facets/ERC20BurnFacet.sol"; -import {IDiamondCut} from "@compose/diamond-protocol/contracts/interfaces/IDiamondCut.sol"; - -contract ERC20BurnFacetDeployment { - address constant DIAMOND_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.erc20")))); - - function deployERC20BurnFacet() public returns (address facet) { - facet = address(new ERC20BurnFacet()); - - // Example: Add ERC20BurnFacet to the diamond - // IDiamondCut(diamondAddress).diamondCut(diamondCutCalldata, address(0), ""); - - return facet; - } - - // Example: Calling burnFrom - function burnSomeTokens(address diamondAddress, address _spender, address _from, uint256 _amount) public { - bytes4 selector = IERC20BurnFacet.burnFrom.selector; - - // Calldata for burnFrom - bytes memory data = abi.encodeWithSelector(selector, _from, _amount); - - // Assuming _spender has an allowance from _from - // Call the diamond proxy to execute burnFrom - (bool success, bytes memory returnData) = diamondAddress.call(data); - require(success, "Burn failed"); - } -}`} - - -## Best Practices - - -- Initialize the ERC20BurnFacet with the correct diamond storage slot address during deployment. -- Ensure the caller has sufficient allowance if using `burnFrom`. -- Access the facet through the diamond proxy address for all interactions. - - -## Security Considerations - - -The `burn` function is permissionless and reduces total supply. The `burnFrom` function requires proper allowance management to prevent unintended token burning. Ensure the diamond's access control mechanisms are correctly configured for any administrative functions related to token supply if applicable. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx deleted file mode 100644 index f438b8fa..00000000 --- a/website/docs/contracts/facets/ERC20Facet.mdx +++ /dev/null @@ -1,571 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "ERC-20 fungible token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 fungible token facet for Compose diamonds - - - -- Full ERC-20 compliance, enabling seamless integration with wallets and DeFi protocols. -- Provides standard token metadata (`name`, `symbol`, `decimals`) and core transfer logic. -- Supports token approvals for third-party spending via `approve` and `transferFrom`. - - -## Overview - -The ERC20Facet implements the ERC-20 fungible token standard for Compose diamonds. It provides standard token operations like name, symbol, transfers, and approvals, making the diamond a compliant ERC-20 token. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Facet} from "@compose/contracts/src/facets/ERC20Facet.sol"; - -contract ERC20Deployer { - address immutable diamondProxy; - - constructor(address _diamondProxy) { - diamondProxy = _diamondProxy; - } - - function getTokenName() external view returns (string memory) { - // Selector for name() - bytes4 selector = IERC20Facet.name.selector; - (bool success, bytes memory data) = diamondProxy.call(abi.encodeWithSelector(selector)); - require(success, "ERC20Facet: name call failed"); - return abi.decode(data, (string)); - } - - function getTokenBalance(address _account) external view returns (uint256) { - // Selector for balanceOf() - bytes4 selector = IERC20Facet.balanceOf.selector; - (bool success, bytes memory data) = diamondProxy.call(abi.encodeWithSelector(selector, _account)); - require(success, "ERC20Facet: balanceOf call failed"); - return abi.decode(data, (uint256)); - } -}`} - - -## Best Practices - - -- Initialize the ERC20Facet with the correct ERC-20 storage slot during diamond deployment. -- Ensure appropriate access control is configured at the diamond level for sensitive functions like `approve` and `transferFrom` if necessary, though standard ERC-20 is typically permissionless. -- When upgrading, ensure the ERC20Facet's storage layout remains compatible to prevent data corruption. - - -## Security Considerations - - -Standard ERC-20 token risks apply, including potential reentrancy if custom logic interacts with `transfer` or `transferFrom` without proper checks. Input validation is handled internally by the facet. Ensure the diamond's access control layer does not inadvertently grant unauthorized access to administrative functions if they were to be added in the future. The `approve` function can be front-run; users should be aware of this standard ERC-20 behavior. The `getStorage` function uses inline assembly to access storage, which requires careful auditing to ensure correctness and prevent unintended state manipulation. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx deleted file mode 100644 index 40142735..00000000 --- a/website/docs/contracts/facets/ERC20PermitFacet.mdx +++ /dev/null @@ -1,339 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "ERC-20 token permit facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token permit facet for Compose diamonds - - - -- Implements EIP-2612 `permit` functionality for gasless allowance approvals. -- Provides `nonces` to track the number of permit usages per owner, preventing replay attacks. -- Exposes `DOMAIN_SEPARATOR` for correct EIP-712 signature hashing. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant token permits within a Compose diamond. It allows users to grant token allowances to third parties via signed off-chain messages, reducing the need for direct on-chain approvals and improving user experience. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - ---- -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; -import {ERC20PermitFacet} from "../facets/ERC20PermitFacet.sol"; - -contract ERC20PermitDeployment { - address public diamondAddress; - - function deploy() public { - // Assume diamondAddress is already set or deployed - diamondAddress = address(this); // Placeholder - - // In a real deployment, you would add the ERC20PermitFacet to the diamond. - // Example (conceptual): - // DiamondCutFacet(diamondAddress).diamondCut(...); - } - - function grantPermit(address _owner, address _spender, uint256 _value, uint256 _deadline, bytes calldata _signature) public { - // Assume the ERC20PermitFacet is already deployed and added to the diamond. - // The selector for permit is 0x6cc17c7b - (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(ERC20PermitFacet.permit.selector, _owner, _spender, _value, _deadline, _signature)); - require(success, "Permit call failed"); - } - - function getPermitNonces(address _owner) public view returns (uint256) { - // The selector for nonces is 0x151662e8 - (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(ERC20PermitFacet.nonces.selector, _owner)); - require(success, "Nonces call failed"); - return abi.decode(data, (uint256)); - } -}`} - - -## Best Practices - - -- Initialize the `DOMAIN_SEPARATOR` and `name`/`version` as part of the diamond's initialization process to ensure correct signature verification. -- Ensure the `ERC20PermitFacet` is added to the diamond before attempting to call its functions. -- Users must correctly construct the EIP-712 domain separator and message for signing, and provide a valid signature to the `permit` function. - - -## Security Considerations - - -The `permit` function relies on off-chain signatures. Ensure that the owner's private key is kept secure. The `deadline` parameter must be checked by the caller to prevent stale permits from being used. Reentrancy is not a concern for the `permit` function itself, as it only modifies allowances and nonces, and does not make external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx deleted file mode 100644 index 6c22ebc6..00000000 --- a/website/docs/contracts/facets/ERC6909Facet.mdx +++ /dev/null @@ -1,531 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "ERC-6909 minimal multi-token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-6909 minimal multi-token facet for Compose diamonds - - - -- Implements a minimal ERC-6909 interface for multi-token management. -- Supports efficient querying of balances, allowances, and operator statuses. -- Enables core token operations: transfer, transferFrom, and approve. - - -## Overview - -The ERC6909Facet implements a minimal multi-token standard for Compose diamonds, enabling efficient management of various token types within a single diamond proxy. It provides essential functions for tracking balances, allowances, operator statuses, and executing token transfers and approvals. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Facet} from "@compose/contracts/src/facets/ERC6909/IERC6909Facet.sol"; -import {ERC6909Facet} from "@compose/contracts/src/facets/ERC6909/ERC6909Facet.sol"; - -contract ERC6909Diamond { - // ... diamond implementation ... - - function erc6909() public view returns (IERC6909Facet) { - // Replace with your diamond's selector mapping - address facetAddress = address(this); // Placeholder - return IERC6909Facet(facetAddress); - } - - function exampleUsage() public { - // Get balance of token ID 1 for the caller - uint256 balance = erc6909().balanceOf(msg.sender, 1); - - // Approve spender for token ID 2 - erc6909().approve(msg.sender, 2, 100); - - // Transfer token ID 3 from caller to a receiver - erc6909().transfer(msg.sender, receiver, 3, 50); - - // Set caller as an operator for token ID 4 - erc6909().setOperator(msg.sender, 4, true); - } -}`} - - -## Best Practices - - -- Initialize the ERC6909Facet with correct storage slot configurations during diamond deployment. -- Ensure that access control for functions like `setOperator` is handled appropriately by the diamond's access control mechanism. -- When upgrading, ensure the storage layout remains compatible according to EIP-2535. - - -## Security Considerations - - -Access control for sensitive functions like `transferFrom` and `approve` should be managed by the diamond's access control system. Ensure that the `setOperator` function does not grant excessive permissions unintentionally. Reentrancy is not a direct concern within this facet's functions as they do not make external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx deleted file mode 100644 index 14443fe2..00000000 --- a/website/docs/contracts/facets/ERC721BurnFacet.mdx +++ /dev/null @@ -1,215 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "ERC-721 NFT burn facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 NFT burn facet for Compose diamonds - - - -- Enables the destruction of ERC721 tokens, permanently removing them from circulation. -- Integrates seamlessly with the Compose diamond storage pattern for ERC721 state management. -- Provides a dedicated function (`burn`) for token destruction, adhering to ERC721 standards. - - -## Overview - -The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens within a Compose diamond. It integrates with the diamond's storage pattern to manage token state and enumeration during the burn process. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "@compose/contracts/src/facets/ERC721/IERC721BurnFacet.sol"; - -contract ERC721BurnDiamondExample { - address constant BURN_FACET_ADDRESS = address(0x...); // Address of the deployed ERC721BurnFacet - - IERC721BurnFacet private _burnFacet; - - function initialize() external { - // Assuming the diamond proxy is already deployed and initialized with other facets - // Add the ERC721BurnFacet to the diamond proxy - // ... diamond.diamondCut(...) ... - _burnFacet = IERC721BurnFacet(BURN_FACET_ADDRESS); - } - - function burnToken(uint256 tokenId) external { - // Call the burn function through the diamond proxy - // In a real scenario, you would call this via the diamond proxy address - // For simplicity, directly calling the facet address here - _burnFacet.burn(tokenId); - } -}`} - - -## Best Practices - - -- Ensure the ERC721BurnFacet is correctly added to the diamond proxy during deployment or upgrade. -- Implement robust access control within your diamond's logic to restrict who can call the `burn` function, typically requiring ownership of the token. -- Use `getStorage()` if direct access to the ERC721 storage is needed for off-chain indexing or complex off-chain operations, understanding the storage slot. - - -## Security Considerations - - -Access control for the `burn` function is paramount. Ensure that only the owner of the token or an authorized entity can initiate a burn. The facet itself does not enforce ownership checks; this logic must be implemented in the calling contract or facet that routes to `burn`. Reentrancy is not a direct concern with the `burn` function as it does not make external calls after state changes. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index 2a805201..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,233 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "ERC-721 NFT enumerableburn facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 NFT enumerableburn facet for Compose diamonds - - - -- Enables burning of ERC721 tokens directly on the diamond. -- Maintains internal token enumeration integrity after token destruction. -- Provides access to the facet's internal storage layout for advanced use cases. - - -## Overview - -The ERC721EnumerableBurnFacet extends ERC721 functionality by providing the ability to burn NFTs while maintaining enumeration tracking. It allows for the removal of tokens from the diamond's state and ensures that the internal token lists remain consistent. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondProxy} from "@compose-protocol/diamond-proxy/contracts/DiamondProxy.sol"; -import {IERC721EnumerableBurnFacet} from "./interfaces/IERC721EnumerableBurnFacet.sol"; - -contract Deployer { - function deploy() external { - // Assume diamondProxy is an already deployed DiamondProxy instance - DiamondProxy diamondProxy; - - // Get the facet implementation address (replace with actual deployment logic) - address erc721EnumerableBurnFacetImpl = address(0x...'); - - // Add the facet to the diamond - // (Requires DiamondCutFacet to be accessible and authorized) - // diamondProxy.diamondCut(...); - - // Interact with the facet through the diamond proxy - IERC721EnumerableBurnFacet enumerableBurnFacet = IERC721EnumerableBurnFacet(diamondProxy); - - // Example: Burn token ID 1 - address from = msg.sender; - uint256 tokenId = 1; - enumerableBurnFacet.burn(from, tokenId); - } -}`} - - -## Best Practices - - -- Ensure the ERC721EnumerableBurnFacet is correctly added to the diamond via a `diamondCut` operation before attempting to use its functions. -- The `burn` function requires the caller to be the owner of the token or an approved address, adhering to standard ERC721 authorization rules. -- Access the facet's storage struct using the `getStorage` function for introspection or debugging if necessary. - - -## Security Considerations - - -The `burn` function must enforce standard ERC721 ownership and approval checks to prevent unauthorized token destruction. Ensure that the diamond's access control mechanisms correctly delegate calls to this facet. Reentrancy is not a direct concern for the `burn` function itself, as it primarily modifies state and does not make external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx deleted file mode 100644 index 4e7c4b05..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,749 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "ERC-721 NFT enumerable facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 NFT enumerable facet for Compose diamonds - - - -- Provides standard ERC-721 metadata (name, symbol, tokenURI). -- Tracks token ownership and balances efficiently. -- Supports both direct and safe token transfers, including receiver contract checks. -- Offers enumerable functions (`tokenOfOwnerByIndex`, `totalSupply`, `balanceOf`) for querying token collections. - - -## Overview - -The ERC721EnumerableFacet provides comprehensive ERC-721 functionality to a Compose diamond, including standard token metadata, ownership tracking, approvals, and enumerable methods to list tokens. It orchestrates the core state management for non-fungible tokens within the diamond's extensible architecture. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableFacet} from "@compose-protocol/diamond-contracts/facets/ERC721/IERC721EnumerableFacet.sol"; -import {DiamondProxy} from "@compose-protocol/diamond-contracts/DiamondProxy.sol"; - -contract ERC721EnumerableConsumer is DiamondProxy { - function mintToken(address _to, uint256 _tokenId) public { - // Assuming ERC721EnumerableFacet is already deployed and added to the diamond - // The function \`mintToken\` is not part of ERC721EnumerableFacet, but would be implemented - // in a custom facet that calls into internalTransferFrom if needed, or directly manages state. - // For demonstration, we assume a mechanism exists to set initial ownership. - - // Example of calling functions from the facet: - IERC721EnumerableFacet erc721 = IERC721EnumerableFacet(address(this)); - - // To actually mint, you'd typically have a dedicated minting facet - // that utilizes internalTransferFrom or similar internal logic. - // This example focuses on demonstrating calls to existing functions. - - // erc721.approve(_to, _tokenId); // Example approval - - // A placeholder for actual minting logic that would set owner and token IDs - // For a real mint, you would interact with the diamond's storage directly - // or via a dedicated minting facet. - - // Example: Querying token details - uint256 ownerTokenCount = erc721.balanceOf(_to); - // address owner = erc721.ownerOf(_tokenId); - // string memory uri = erc721.tokenURI(_tokenId); - } - - function getTokenOwnerByIndex(address _owner, uint256 _index) public view returns (uint256) { - IERC721EnumerableFacet erc721 = IERC721EnumerableFacet(address(this)); - return erc721.tokenOfOwnerByIndex(_owner, _index); - } -}`} - - -## Best Practices - - -- Initialize the ERC721EnumerableFacet with a name and symbol during diamond deployment or via an initialization function. -- Ensure appropriate access control is implemented in facets that call `approve`, `transferFrom`, or `safeTransferFrom` to prevent unauthorized token movements. -- When upgrading, maintain storage layout compatibility to avoid data corruption, especially for mapping and array structures. - - -## Security Considerations - - -This facet implements standard ERC-721 transfer logic. Ensure that the calling facets correctly validate `msg.sender` and approved addresses before invoking transfer functions (`transferFrom`, `safeTransferFrom`) to prevent unauthorized token transfers. Reentrancy is mitigated by the diamond's proxy pattern and typical ERC-721 implementation patterns where state changes precede external calls within a single function execution. Input validation for token IDs and addresses is crucial in any custom facets interacting with this facet. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx deleted file mode 100644 index 0afc3070..00000000 --- a/website/docs/contracts/facets/ERC721Facet.mdx +++ /dev/null @@ -1,669 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "ERC-721 non-fungible token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 non-fungible token facet for Compose diamonds - - - -- Full ERC-721 compliance, enabling standard non-fungible token interactions. -- Supports both direct transfers and safe transfers, including checks for receiver contract compatibility. -- Provides essential query functions for token ownership, balances, and approvals. - - -## Overview - -The ERC721Facet provides a robust implementation of the ERC-721 non-fungible token standard within a Compose diamond. It enables the management and transfer of unique digital assets, exposing essential querying and mutation functions for token ownership, approvals, and metadata. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose-protocol/diamond/contracts/facets/ERC721Facet.sol"; - -contract ERC721Consumer { - address immutable DIAMOND_ADDRESS; - - constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; - } - - function getTokenName() external view returns (string memory) { - IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); - return erc721.name(); - } - - function getTokenSymbol() external view returns (string memory) { - IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); - return erc721.symbol(); - } - - function getTokenOwner(uint256 tokenId) external view returns (address) { - IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); - return erc721.ownerOf(tokenId); - } - - function approveToken(address to, uint256 tokenId) external { - IERC721Facet erc721 = IERC721Facet(DIAMOND_ADDRESS); - erc721.approve(to, tokenId); - } -}`} - - -## Best Practices - - -- Ensure the ERC721Facet is correctly initialized with the appropriate storage slot during diamond deployment or upgrade. -- Utilize the `internalTransferFrom` function internally when implementing custom transfer logic to leverage built-in ownership and approval checks. -- Be mindful of gas costs when calling functions that iterate over token balances or approvals, especially for large token supplies. - - -## Security Considerations - - -The `safeTransferFrom` functions include checks to ensure the receiving address can handle ERC-721 tokens, mitigating risks associated with sending tokens to incompatible contracts. Direct transfers (`transferFrom`) do not perform this check. Access control for approval functions (`approve`, `setApprovalForAll`) is implicitly handled by ERC-721 ownership rules. Reentrancy is not a direct concern for the core ERC-721 functions themselves, but custom logic interacting with this facet should be audited for reentrancy vulnerabilities. - - -
- -
- - diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx deleted file mode 100644 index bded85ed..00000000 --- a/website/docs/contracts/facets/ExampleDiamond.mdx +++ /dev/null @@ -1,150 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Diamond core facet for ERC-2535 implementation" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond core facet for ERC-2535 implementation - - - -- Manages facet registration and function selector mapping according to ERC-2535. -- Acts as the central dispatcher, delegating calls to the correct facet via `delegatecall`. -- Supports Add, Replace, and Remove actions for facets during initialization and upgrades. - - -## Overview - -The ExampleDiamond contract serves as the core implementation of the ERC-2535 Diamond Standard. It manages facet registration, function selector mapping, and delegates calls to the appropriate facets, acting as the central routing mechanism for all diamond functionality. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ExampleDiamond} from "@compose/contracts/src/diamond/ExampleDiamond.sol"; -import {IDiamondCut} from "@compose/contracts/src/diamond/interfaces/IDiamondCut.sol"; - -// Assume other facets and their selectors are defined elsewhere -import {MyFacetA} from "./MyFacetA.sol"; -import {MyFacetB} from "./MyFacetB.sol"; - -contract DeployExampleDiamond { - address public diamondAddress; - - function deploy() public { - // Define facet cuts for deployment - IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](2); - - // Facet A cut - facetCuts[0] = IDiamondCut.FacetCut({ - facetAddress: address(new MyFacetA()), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: MyFacetA.getSelectors() - }); - - // Facet B cut - facetCuts[1] = IDiamondCut.FacetCut({ - facetAddress: address(new MyFacetB()), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: MyFacetB.getSelectors() - }); - - // Deploy the diamond, passing the initial facet cuts and owner - ExampleDiamond deployedDiamond = new ExampleDiamond(facetCuts, msg.sender); - diamondAddress = address(deployedDiamond); - } - - // Example of calling a function through the diamond - function callFacetA(address _diamondAddress) public { - // Assume MyFacetA has a function \`doSomething()\` - // The diamond's fallback or receive will handle routing - // This is illustrative; actual calls use the diamond's proxy address - (bool success, ) = _diamondAddress.call(abi.encodeWithSignature("doSomething()", MyFacetA.getSelectors()[0])); - require(success, "Call to Facet A failed"); - } -}`} - - -## Best Practices - - -- Initialize the diamond with all necessary facets during deployment using the `constructor` to ensure a functional state from the outset. -- Carefully manage the `FacetCutAction` enum (Add, Replace, Remove) to control facet updates during upgrades. -- Ensure that facet addresses provided during initialization are verified and trusted to prevent malicious code injection. - - -## Security Considerations - - -The constructor is critical for initial setup; ensure that only trusted facet addresses and selectors are provided. The `fallback` and `receive` functions are responsible for routing external calls, making them potential targets for reentrancy if not implemented carefully within the facets themselves. Input validation should be handled within individual facets, not the core diamond contract. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx deleted file mode 100644 index 6d5d56fa..00000000 --- a/website/docs/contracts/facets/OwnerFacet.mdx +++ /dev/null @@ -1,213 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "Ownership management facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Ownership management facet for Compose diamonds - - - -- Provides a standard interface for diamond ownership management. -- Supports transferring ownership to a new address. -- Allows for the complete renouncement of ownership. - - -## Overview - -The OwnerFacet provides essential ownership management capabilities for a Compose diamond. It allows for the retrieval of the current owner and facilitates the transfer or renouncement of ownership, ensuring controlled administration of the diamond's core functions. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose-protocol/diamond-core/contracts/facets/Owner/IOwnerFacet.sol"; -import {DiamondProxy} from "@compose-protocol/diamond-core/contracts/diamond/DiamondProxy.sol"; - -contract OwnerFacetUser { - IOwnerFacet ownerFacet; - - constructor(address diamondProxyAddress) { - ownerFacet = IOwnerFacet(diamondProxyAddress); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function transferDiamondOwnership(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function renounceDiamondOwnership() external { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize ownership with a trusted address during diamond deployment. -- Use `transferOwnership` to designate a new owner and confirm the transfer by the new owner calling `transferOwnership` with their address. -- Grant `transferOwnership` and `renounceOwnership` permissions to a secure administrative role or the current owner. - - -## Security Considerations - - -Access to `transferOwnership` and `renounceOwnership` must be strictly controlled to prevent unauthorized changes to diamond administration. Ensure that the address set as the new owner is verified before the transfer is finalized. Renouncing ownership should be done with extreme caution as it permanently relinquishes control. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx deleted file mode 100644 index 1ceb5213..00000000 --- a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,292 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "Two-step ownership transfer facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Two-step ownership transfer facet for Compose diamonds - - - -- Implements a secure two-step ownership transfer mechanism. -- Allows querying of current owner and pending owner addresses. -- Provides explicit functions for `transferOwnership`, `acceptOwnership`, and `renounceOwnership`. - - -## Overview - -The OwnerTwoStepsFacet manages the ownership of a Compose diamond through a secure two-step transfer process. It provides functions to view the current and pending owner, initiate a transfer, and accept or renounce ownership, ensuring robust control over administrative privileges. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - ---- -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "@compose/diamond/contracts/facets/ownership/IOwnerTwoStepsFacet.sol"; -import {DiamondProxy} from "@compose/diamond/contracts/DiamondProxy.sol"; - -contract OwnerTwoStepsFacetUser { - IOwnerTwoStepsFacet ownerFacet; - - constructor(address diamondProxyAddress) { - ownerFacet = IOwnerTwoStepsFacet(diamondProxyAddress); - } - - function transferOwnershipTo(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function acceptOwnershipFrom(address _currentOwner) external { - ownerFacet.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function getPendingOwner() external view returns (address) { - return ownerFacet.pendingOwner(); - } -}`} - - -## Best Practices - - -- Initialize ownership transfers using `transferOwnership` and confirm with `acceptOwnership` to prevent accidental loss of control. -- Ensure the diamond proxy address is correctly set when interacting with the facet. -- Use `renounceOwnership` only when the contract is intended to become unowned. - - -## Security Considerations - - -The `transferOwnership` function sets a pending owner. The ownership is only fully transferred once the new owner calls `acceptOwnership`. This prevents ownership from being transferred to an incorrect or inaccessible address. There are no reentrancy concerns as these functions do not make external calls. Input validation is handled by the Solidity type system for addresses. - - -
- -
- - diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx deleted file mode 100644 index c597b924..00000000 --- a/website/docs/contracts/facets/RoyaltyFacet.mdx +++ /dev/null @@ -1,199 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "ERC-2981 royalty facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2981 royalty facet for Compose diamonds - - - -- Implements the ERC-2981 `royaltyInfo` standard. -- Supports token-specific royalty configurations. -- Provides a fallback to a default royalty setting. -- Utilizes inline assembly for efficient storage access. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard, enabling composable royalty payments within a Compose diamond. It provides a standardized interface for querying royalty information for specific tokens, facilitating revenue sharing for creators and secondary market participants. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyFacet} from "@compose/core/facets/RoyaltyFacet.sol"; -import {IDiamond} from "@compose/core/interfaces/IDiamond.sol"; - -contract RoyaltyConsumer { - IDiamond immutable diamondProxy; - - constructor(address _diamondProxy) { - diamondProxy = IDiamond(_diamondProxy); - } - - function getTokenRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { - // Get the RoyaltyFacet selector - bytes4 royaltySelector = IRoyaltyFacet.royaltyInfo.selector; - - // Call the royaltyInfo function through the diamond proxy - (bool success, bytes memory result) = address(diamondProxy).call(abi.encodeWithSelector(royaltySelector, _tokenId, _salePrice)); - require(success, "RoyaltyFacet: royaltyInfo call failed"); - - // Decode the result - (receiver, royaltyAmount) = abi.decode(result, (address, uint256)); - return (receiver, royaltyAmount); - } -}`} - - -## Best Practices - - -- Initialize the RoyaltyFacet with appropriate default royalty settings during diamond deployment. -- Ensure that token-specific royalty configurations are set correctly using the underlying storage mechanism. -- When upgrading, preserve the `STORAGE_POSITION` for the royalty storage struct to maintain state continuity. - - -## Security Considerations - - -The `royaltyInfo` function is read-only and does not introduce reentrancy risks. Access control for setting default and token-specific royalties should be managed at the diamond level or through a dedicated administrative facet. Ensure the `STORAGE_POSITION` constant is unique and does not conflict with other facets. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx deleted file mode 100644 index fc806118..00000000 --- a/website/docs/contracts/modules/AccessControlMod.mdx +++ /dev/null @@ -1,451 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Role-based access control (RBAC) module for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control (RBAC) module for Compose diamonds - - - -- Standardized RBAC implementation for consistent permission management across facets. -- Functions for granting, revoking, and checking roles, as well as setting role administrators. -- Built-in check (`requireRole`) that reverts with a specific error on unauthorized access. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlMod provides robust role-based access control (RBAC) for Compose diamonds. It enables fine-grained permission management, ensuring that only authorized accounts can execute critical functions, thereby enhancing the security and integrity of diamond operations. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlMod} from "@compose/modules/AccessControlMod.sol"; - -contract MyFacet { - IAccessControlMod internal accessControlMod; - - constructor(address _accessControlModAddress) { - accessControlMod = IAccessControlMod(_accessControlModAddress); - } - - // Example role: DEFAULT_ADMIN_ROLE - bytes32 public constant DEFAULT_ADMIN_ROLE = keccak256("DEFAULT_ADMIN_ROLE"); - bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); - - function grantManagerRole(address _account) external { - // Only the admin can grant the manager role - accessControlMod.requireRole(_account, DEFAULT_ADMIN_ROLE); - accessControlMod.grantRole(MANAGER_ROLE, _account); - } - - function performManagerAction() external { - // Only users with the MANAGER_ROLE can perform this action - accessControlMod.requireRole(msg.sender, MANAGER_ROLE); - // ... manager action logic ... - } -}`} - - -## Best Practices - - -- Always use custom errors provided by the AccessControlMod for revert conditions to ensure gas efficiency and clarity. -- When defining roles, use `keccak256` on a descriptive string for immutability and uniqueness. -- Ensure the AccessControlMod is initialized with appropriate admin roles during deployment to secure the access control system itself. - - -## Integration Notes - - -The AccessControlMod utilizes its own dedicated storage slot within the diamond. Facets interact with the module via its interface. Changes to role assignments or role admin configurations are immediately reflected and visible to all facets querying the module's functions. When adding the AccessControlMod as a facet, ensure its storage is initialized correctly and that the `DEFAULT_ADMIN_ROLE` is assigned to the appropriate deployer or owner account. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx deleted file mode 100644 index da0dfd98..00000000 --- a/website/docs/contracts/modules/AccessControlPausableMod.mdx +++ /dev/null @@ -1,405 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Role-based access control with pause functionality for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control with pause functionality for Compose diamonds - - - -- Role-based authorization: Enforces that only accounts assigned specific roles can execute protected functions. -- Pause functionality: Allows for temporary suspension of role execution, providing an emergency stop mechanism. -- Diamond-native integration: Designed to seamlessly integrate with the Compose diamond proxy pattern and its storage management. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides robust role-based access control combined with pause functionality for Compose diamonds. It ensures that sensitive operations can be restricted to authorized roles and temporarily halted when necessary, enhancing security and operational control within a diamond. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausableMod} from "@compose/diamond-contracts/contracts/modules/AccessControlPausableMod.sol"; - -contract MyFacet { - // Assuming AccessControlPausableMod is deployed at this address - IAccessControlPausableMod internal accessControlPausableMod; - - constructor(address _accessControlPausableModAddress) { - accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableModAddress); - } - - /** - * @notice Pauses a specific role, preventing any further execution of functions protected by that role. - * @param _role The role to pause. - */ - function pauseMyRole(bytes32 _role) external { - // Example: Only an admin can pause a role - // require(msg.sender == diamond.owner(), \"Not owner\"); - accessControlPausableMod.pauseRole(_role); - } - - /** - * @notice Unpauses a specific role, allowing functions protected by that role to be executed again. - * @param _role The role to unpause. - */ - function unpauseMyRole(bytes32 _role) external { - // Example: Only an admin can unpause a role - // require(msg.sender == diamond.owner(), \"Not owner\"); - accessControlPausableMod.unpauseRole(_role); - } - - /** - * @notice Checks if a given role is currently paused. - * @param _role The role to check. - * @return bool True if the role is paused, false otherwise. - */ - function isMyRolePaused(bytes32 _role) external view returns (bool) { - return accessControlPausableMod.isRolePaused(_role); - } - - /** - * @notice Requires that an account has a specific role and that the role is not currently paused. - * @param _role The role to check. - * @param _account The account to check. - */ - function executeActionWithRole(bytes32 _role, address _account) external { - accessControlPausableMod.requireRoleNotPaused(_role, _account); - // ... execute sensitive action ... - } -} -`} - - -## Best Practices - - -- Ensure that only authorized entities can call `pauseRole` and `unpauseRole` functions, typically through an admin role managed by the diamond's ownership pattern. -- Thoroughly test the `requireRoleNotPaused` function in conjunction with your facet's access-controlled logic to prevent unauthorized or paused role executions. -- Be mindful of upgradeability: changes to the underlying storage layout of this module may require careful migration strategies to maintain state consistency across diamond upgrades. - - -## Integration Notes - - -This module interacts with two distinct storage areas within the diamond: `AccessControl` storage and `AccessControlPausable` storage. Facets that utilize this module will typically call its public functions. The `requireRoleNotPaused` function performs checks against both role membership and pause status, reverting with specific errors (`AccessControlUnauthorizedAccount`, `AccessControlRolePaused`) if conditions are not met. Facets should ensure they have access to the correct storage slots for these internal structs if they need to directly inspect or manipulate role assignments or pause states. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx deleted file mode 100644 index e7fba01e..00000000 --- a/website/docs/contracts/modules/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,504 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Time-limited role-based access control for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Time-limited role-based access control for Compose diamonds - - - -- Time-limited role assignments: Grants roles that automatically expire after a specified timestamp. -- Temporal role revocation: Allows for immediate removal of a role before its expiry. -- Role expiry checking: Provides functions to query the expiry status of role assignments. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlTemporalMod provides time-limited role-based access control, enabling granular permission management within Compose diamonds. This module is crucial for scenarios requiring temporary privileges, enhancing security and operational flexibility by automatically revoking access after a specified duration. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -Storage position: `ACCESS_CONTROL_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/modules/AccessControlTemporalMod/IAccessControlTemporalMod.sol"; -import {DiamondStorage, DiamondFacet, AccessControlUnauthorizedAccount, AccessControlRoleExpired} from "@compose/core/"; - -contract MyFacet is DiamondFacet { - IAccessControlTemporalMod internal constant accessControlTemporalMod = IAccessControlTemporalMod(address(this)); - - // Role definition (example) - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - /** - * @notice Grants an operator role to an address with a specific expiry. - * @param _account The address to grant the role to. - * @param _expiry The timestamp when the role expires. - */ - function grantOperatorRole(address _account, uint64 _expiry) external { - accessControlTemporalMod.grantRoleWithExpiry(OPERATOR_ROLE, _account, _expiry); - } - - /** - * @notice Revokes a temporal role from an address. - * @param _role The role to revoke. - * @param _account The address to revoke the role from. - */ - function revokeOperatorRole(bytes32 _role, address _account) external { - accessControlTemporalMod.revokeTemporalRole(_role, _account); - } - - /** - * @notice Requires that the caller has a valid, non-expired operator role. - */ - function performOperation() external { - accessControlTemporalMod.requireValidRole(OPERATOR_ROLE, msg.sender); - // Operation logic here - } - - /** - * @notice Checks if a role has expired. - * @param _role The role to check. - * @param _account The account assigned the role. - * @return bool True if the role has expired, false otherwise. - */ - function checkRoleExpiry(bytes32 _role, address _account) external view returns (bool) { - return accessControlTemporalMod.isRoleExpired(_role, _account); - } -} -`} - - -## Best Practices - - -- Always use `requireValidRole` to enforce temporal access before critical operations, handling `AccessControlUnauthorizedAccount` and `AccessControlRoleExpired` errors. -- When granting roles, ensure the `_expiry` timestamp is set appropriately to prevent indefinite access and manage temporary permissions effectively. -- Use `revokeTemporalRole` for immediate revocation of roles before their natural expiry if circumstances change. - - -## Integration Notes - - -The AccessControlTemporalMod interacts with the diamond's storage to manage role assignments and their expiry timestamps. Facets using this module will typically call its external functions. The module's storage is distinct and managed independently, but its state (role assignments and expiry) directly impacts the access control checks performed by facets. Ensure the `AccessControlTemporalMod` facet is correctly initialized and accessible within the diamond's facet registry. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx deleted file mode 100644 index c838e3e7..00000000 --- a/website/docs/contracts/modules/DiamondCutMod.mdx +++ /dev/null @@ -1,379 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Diamond upgrade (cut) module for ERC-2535 diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond upgrade (cut) module for ERC-2535 diamonds - - - -- Supports adding, replacing, and removing functions via function selectors and facet addresses. -- Allows for atomic upgrades by enabling the execution of a function immediately after the cut operation via `delegatecall`. -- Provides a mechanism to retrieve the storage layout of the diamond. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCutMod provides essential functionality for managing facets within an ERC-2535 Diamond Proxy. It enables developers to add, replace, and remove functions, facilitating upgrades and modularity. This module is crucial for dynamic diamond evolution, allowing for safe and controlled modifications to the diamond's behavior. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/contracts/src/diamond/IDiamondCut.sol"; - -contract MyFacet { - address constant DIAMOND_CUT_FACET_ADDRESS = address(0x1234567890abcdef1234567890abcdef1234567890); // Replace with actual address - IDiamondCut immutable diamondCutFacet; - - constructor(address _diamondCutFacetAddress) { - diamondCutFacet = IDiamondCut(_diamondCutFacetAddress); - } - - function upgradeFacet(bytes4[] memory _selectors, address _newFacetAddress) external { - // Assuming this facet has the necessary permissions to call diamondCut - // For demonstration, we are only replacing functions, not adding/removing - diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](1)[]({ - facetAddress: _newFacetAddress, - action: IDiamondCut.FacetAction.Replace, - selectors: _selectors - }), address(0), ""); - } -}`} - - -## Best Practices - - -- Ensure that any facet calling `diamondCut` has the appropriate access control (e.g., is an owner or authorized role) as this function can significantly alter the diamond's functionality. -- Carefully manage function selectors when adding, replacing, or removing them to avoid unintended behavior or orphaned functions. -- Understand the implications of `diamondCut`'s `execute` functionality; only use it with trusted functions and data, as it performs a `delegatecall`. - - -## Integration Notes - - -The DiamondCutMod interacts directly with the diamond proxy's internal storage to map function selectors to facet addresses. Changes made through `diamondCut` are immediately reflected in the diamond's routing logic. Facets that query or rely on the diamond's function routing will automatically see the updated mappings after a successful diamond cut operation. The order of facet additions and removals is critical for maintaining correct storage layout and function accessibility. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx deleted file mode 100644 index 999fa468..00000000 --- a/website/docs/contracts/modules/DiamondMod.mdx +++ /dev/null @@ -1,237 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Diamond Library - Internal functions and storage for diamond proxy functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond Library - Internal functions and storage for diamond proxy functionality. - - - -- Manages facet registration and function selector mapping during diamond deployment (`addFacets`). -- Provides a fallback mechanism (`diamondFallback`) to route external calls to the appropriate facet. -- Allows read access to raw storage slots of the diamond proxy (`getStorage`). - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondMod library provides essential internal functions for managing diamond proxy facets and handling function calls. It is crucial for the composition and operational integrity of a Compose diamond, enabling dynamic facet registration and ensuring correct function dispatch. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -Storage position: `DIAMOND_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondMod} from "@compose/diamond/DiamondMod.sol"; - -contract MyFacet { - DiamondMod internal immutable diamondMod; - - constructor(address _diamondModAddress) { - diamondMod = DiamondMod(_diamondModAddress); - } - - /** - * @notice Example of calling a function within DiamondMod to retrieve storage. - * @return The raw storage slot value. - */ - function readDiamondStorage(uint256 _slot) external view returns (bytes32) { - return diamondMod.getStorage(_slot); - } -}`} - - -## Best Practices - - -- Use `DiamondMod` only during initial diamond deployment for `addFacets`. Its functions are intended for internal proxy operations, not direct external facet interaction after deployment. -- Ensure correct initialization of `DiamondMod` address within facets that require access to diamond proxy state or logic. -- Handle potential errors during function execution via `diamondFallback` if custom error handling is required by your facet logic. - - -## Integration Notes - - -DiamondMod interacts directly with the diamond proxy's storage. The `addFacets` function is designed to be called only during the diamond's initial deployment to register facets and their function selectors. `diamondFallback` is the core dispatch mechanism, finding the correct facet for any incoming call not handled by the proxy itself. `getStorage` provides a low-level view into the diamond's state, directly accessing specified storage slots. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx deleted file mode 100644 index 0e61e399..00000000 --- a/website/docs/contracts/modules/ERC1155Mod.mdx +++ /dev/null @@ -1,616 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens. - - - -- Supports both single and batch transfers of ERC-1155 tokens, ensuring flexibility for various asset types. -- Implements safe transfer logic, including receiver validation for contract addresses, adhering to EIP-1155 standards. -- Provides functionality for setting token URIs, enabling rich metadata for each token ID. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC1155Mod provides a robust implementation of the ERC-1155 Multi-Token Standard, enabling facets to manage fungible and non-fungible tokens within a Compose diamond. This module is crucial for creating complex economies and managing diverse digital assets, ensuring safe transfers and clear metadata handling. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import {ERC1155Storage, ERC1155Facet} from "./facets/ERC1155Facet.sol"; - -contract MyERC1155Consumer is IERC1155Receiver { - address diamondAddress; - ERC1155Facet erc1155Facet; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - erc1155Facet = ERC1155Facet(diamondAddress); - } - - function mintTokens(address _to, uint256 _id, uint256 _amount) external { - erc1155Facet.mint(_to, _id, _amount); - } - - function safeTransfer(address _from, address _to, uint256 _id, uint256 _amount) external { - erc1155Facet.safeTransferFrom(_from, _to, _id, _amount, ""); - } - - // Implement IERC1155Receiver functions as needed - function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) { - return IERC1155Receiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) { - return IERC1155Receiver.onERC1155BatchReceived.selector; - } -}`} - - -## Best Practices - - -- Always validate receiver addresses in `safeTransferFrom` and `safeBatchTransferFrom` to prevent unexpected behavior or reentrancy. -- Ensure proper access control is implemented at the diamond level for functions like `setBaseURI` and `setTokenURI` if they require administrative privileges. -- Be mindful of gas costs when minting or transferring large batches of tokens; consider batching operations where appropriate. - - -## Integration Notes - - -The ERC1155Mod interacts with diamond storage through a predefined storage slot managed by the diamond proxy. Facets using this module will access and modify state variables such as balances, approvals, and token URIs. Changes to these storage variables are directly visible to all facets interacting with the ERC1155 storage struct. The `getStorage` function provides direct access to this struct, allowing for read operations on the ERC-1155 state. Ensure that the ERC1155 storage struct is correctly laid out and initialized within the diamond's storage pattern. Avoid modifying storage slots used by other facets to maintain composability and prevent conflicts. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx deleted file mode 100644 index 087941e3..00000000 --- a/website/docs/contracts/modules/ERC165Mod.mdx +++ /dev/null @@ -1,162 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. - - - -- Provides a standardized mechanism for interface detection via ERC-165. -- Stores interface support data efficiently within the diamond's storage. -- Enables composability by clearly communicating supported functionalities to external agents. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod module provides the necessary storage and logic to comply with the ERC-165 standard for interface detection within a Compose diamond. This is crucial for allowing external contracts to query which interfaces a diamond supports, enhancing interoperability and composability. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC165, IERC165, ERC165Mod} from "@compose/modules/ERC165/LibERC165.sol"; - -contract MyERC721Facet { - /** - * @dev Initializes the ERC721 facet and registers supported interfaces. - */ - function initERC721() external { - // Register the ERC721 interface ID - ERC165Mod.registerInterface(type(IERC721).interfaceId); - - // Other initialization logic for ERC721 facet - } - - /** - * @dev Implements the supportsInterface function from ERC165. - */ - function supportsInterface(bytes4 interfaceId) external view virtual override(IERC165) returns (bool) { - // Check if the interface is registered in the ERC165 storage - return LibERC165.supportsInterface(interfaceId); - } -}`} - - -## Best Practices - - -- Call `ERC165Mod.registerInterface()` during facet initialization to declare supported interfaces. -- Ensure the `supportsInterface` function within your facet correctly delegates to `LibERC165.supportsInterface()`. -- Keep the list of registered interfaces accurate to avoid misleading callers about diamond capabilities. - - -## Integration Notes - - -The ERC165Mod utilizes a dedicated storage slot for its `ERC165Storage` struct. This struct contains a mapping from interface IDs to booleans, indicating support. Facets should call `ERC165Mod.registerInterface()` during their initialization phase to populate this mapping. The `supportsInterface` function, typically implemented in a facet, should query this storage via `LibERC165.supportsInterface()` to return accurate results. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx deleted file mode 100644 index 327434b0..00000000 --- a/website/docs/contracts/modules/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,438 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "LibERC20Bridgeable — ERC-7802 Library" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Bridgeable — ERC-7802 Library - - - -- Enforces access control for cross-chain operations via the `TrustedBridge` role. -- Provides explicit functions for cross-chain token burning and minting. -- Leverages internal helper functions (`checkTokenBridge`, `getAccessControlStorage`, `getERC20Storage`) for efficient and direct access to necessary storage. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides cross-chain ERC20 token bridging functionality, enabling secure burning and minting across different chains. It enforces access control by relying on a 'trusted-bridge' role, ensuring only authorized entities can perform cross-chain operations. This is crucial for maintaining the integrity and security of token transfers in a multi-chain environment. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Bridgeable } from "@compose-protocol/diamond-contracts/contracts/modules/erc20/interfaces/IERC20Bridgeable.sol"; -import { LibAccessControl } from "@compose-protocol/diamond-contracts/contracts/modules/access/LibAccessControl.sol"; - -contract MyFacet is IERC20Bridgeable { - using LibAccessControl for LibAccessControl.AccessControlStorage; - - function crosschainBurn(address _token, address _from, uint256 _amount) external { - // This function is directly callable from the diamond proxy. - // The actual implementation is in the ERC20Bridgeable facet. - // Ensure your facet has a selector for this function. - - // Example of calling the diamond's implementation: - // This is illustrative; direct calls from facets to other facets - // are typically not needed for core functionality. - // The diamond proxy routes calls to the correct facet. - - // To demonstrate the checkTokenBridge logic internally: - LibAccessControl.AccessControlStorage storage acs = LibAccessControl.getAccessControlStorage(); - acs.checkRole(LibAccessControl.Role.TrustedBridge); - - // In a real scenario, the diamond proxy would route this call - // to the ERC20Bridgeable facet's crosschainBurn function. - // The facet itself doesn't need to explicitly call other facets for its own functions. - } - - function crosschainMint(address _token, address _to, uint256 _amount) external { - // Similar to crosschainBurn, the diamond proxy routes this. - LibAccessControl.AccessControlStorage storage acs = LibAccessControl.getAccessControlStorage(); - acs.checkRole(LibAccessControl.Role.TrustedBridge); - } - - // Other facet functions would go here... -}`} - - -## Best Practices - - -- Ensure that only addresses assigned the `TrustedBridge` role can call `crosschainBurn` and `crosschainMint` functions. This role management is handled by the AccessControl module. -- When upgrading facets, be mindful of the storage layout of `AccessControlStorage` and `ERC20Storage` to maintain compatibility and prevent data corruption. -- Implement robust error handling by checking the return values or using custom errors for revert conditions, such as an untrusted bridge caller. - - -## Integration Notes - - -The `ERC20BridgeableMod` interacts with the diamond's storage through two primary storage structs: `AccessControlStorage` and `ERC20Storage`. The `getAccessControlStorage` and `getERC20Storage` functions provide direct access to these structs at their predefined diamond storage slots. The `checkTokenBridge` internal function specifically relies on the `AccessControlStorage` to verify the caller's `TrustedBridge` role. Facets implementing or interacting with this module should ensure they have the correct selectors registered for `crosschainBurn` and `crosschainMint` and that the diamond's storage layout accommodates these structs without conflicts. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx deleted file mode 100644 index 13a266da..00000000 --- a/website/docs/contracts/modules/ERC20Mod.mdx +++ /dev/null @@ -1,426 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic. - - - -- Provides essential ERC-20 functions: `mint`, `burn`, `transfer`, `transferFrom`, and `approve`. -- Manages ERC-20 token supply and balances internally. -- Uses inline assembly via `getStorage` for efficient access to the ERC-20 storage layout, adhering to the diamond storage pattern. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod module provides a standardized internal library for ERC-20 token functionalities. It manages essential token state and operations like minting, burning, transfers, and approvals, enabling composable and upgradeable ERC-20 implementations within a diamond proxy. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod } from "../modules/erc20/interfaces/IERC20Mod.sol"; -import { ERC20Storage } from "../modules/erc20/storage/ERC20Storage.sol"; - -contract MyERC20Facet { - IERC20Mod private constant _ERC20 = IERC20Mod(address(this)); - - // Assume _ERC20.getStorage() correctly resolves to the ERC20Storage struct - // within the diamond's storage layout. - - function mintTokens(address _to, uint256 _amount) external { - // Ensure caller has permission to mint, e.g., via an access control facet - _ERC20.mint(_to, _amount); - } - - function transferTokens(address _from, address _to, uint256 _amount) external { - // Ensure caller has permission to transfer from _from - _ERC20.transferFrom(_from, _to, _amount); - } - - function approveSpender(address _spender, uint256 _amount) external { - _ERC20.approve(msg.sender, _spender, _amount); - } -}`} - - -## Best Practices - - -- Always ensure that access control is handled by a separate facet or within the calling facet before invoking ERC20Mod functions that modify state (e.g., mint, burn, transfer). -- When extending ERC20Mod, ensure new storage variables are added to the end of the `ERC20Storage` struct to maintain compatibility with existing deployments. -- Handle potential `ERC20Errors` (if defined by the specific ERC-20 facet implementation) or check return values for transfer functions to gracefully manage failed operations. - - -## Integration Notes - - -The ERC20Mod library interacts directly with the `ERC20Storage` struct, which must be allocated to a specific storage slot within the diamond proxy's overall storage layout. The `getStorage` function utilizes inline assembly to bind to this fixed slot. Facets that use ERC20Mod must ensure the `ERC20Storage` struct is correctly defined and positioned in the diamond's storage blueprint, and that its internal layout matches the library's expectations. Changes to the `ERC20Storage` struct by other facets could break ERC20Mod functionality if not managed carefully. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx deleted file mode 100644 index 1d2ce4db..00000000 --- a/website/docs/contracts/modules/ERC20PermitMod.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage" -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage - - - -- Implements ERC-2612 permit functionality, enabling gasless token approvals. -- Manages domain separator generation for secure signature validation. -- Provides a clear interface for validating and applying permit signatures. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20PermitMod provides essential ERC-2612 permit functionality, enabling gasless approvals for ERC-20 token transfers. It manages domain separator calculation and permit signature validation, allowing users to delegate token spending authority via signed messages. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -Storage position: `ERC20_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Permit, LibERC20Permit} from "@compose/contracts/src/modules/erc20/permit/LibERC20Permit.sol"; -import {IERC20PermitMod} from "@compose/contracts/src/modules/erc20/permit/ERC20PermitMod.sol"; - -contract MyERC20Facet { - using LibERC20Permit for IERC20Permit; - IERC20PermitMod private immutable _erc20PermitMod; - - constructor(address _erc20PermitModAddress) { - _erc20PermitMod = IERC20PermitMod(_erc20PermitModAddress); - } - - /** - * @notice Approves an amount of tokens to a spender using a permit signature. - * @param _owner The owner of the tokens. - * @param _spender The address to approve. - * @param _value The amount of tokens to approve. - * @param _deadline The permit deadline. - * @param _v The v component of the signature. - * @param _r The r component of the signature. - * @param _s The s component of the signature. - */ - function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { - // The permit function in the module emits the Approval event. - _erc20PermitMod.permit(_owner, _spender, _value, _deadline, _v, _r, _s); - } - - /** - * @notice Get the domain separator. - * @return The domain separator. - */ - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _erc20PermitMod.DOMAIN_SEPARATOR(); - } -} -`} - - -## Best Practices - - -- Ensure the `permit` function is only callable by authorized entities if underlying token logic requires it. The module itself is permissionless regarding signature validation. -- Always verify the `_deadline` to prevent stale permits from being used. -- If extending ERC-20 functionality, ensure the `Approval` event is correctly emitted by the calling facet when `ERC20PermitMod.permit` is called. - - -## Integration Notes - - -The ERC20PermitMod interacts with the diamond's storage pattern to access and potentially update permit-related state. Facets using this module should ensure they correctly initialize the module and call its functions. The `permit` function in this module emits an `Approval` event, which is expected to be handled by the calling facet or the diamond's event aggregation mechanism. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx deleted file mode 100644 index 311231b7..00000000 --- a/website/docs/contracts/modules/ERC6909Mod.mdx +++ /dev/null @@ -1,532 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic. - - - -- Implements essential ERC-6909 multi-token functionalities: mint, burn, transfer, approve, and setOperator. -- Utilizes a standardized storage slot for efficient access and compatibility with diamond upgrades. -- Supports operator functionality, allowing designated addresses to transfer tokens on behalf of others without requiring explicit approval. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -ERC6909Mod provides the core logic and storage for implementing a minimal ERC-6909 multi-token standard within a Compose diamond. This module enables efficient management of multiple token types, including minting, burning, transfers, and approvals, all managed through a standardized storage pattern compatible with diamond upgrades. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Storage, ERC6909Mod} from "@compose/modules/erc6909/ERC6909.sol"; - -contract MyERC6909Facet { - IERC6909Storage private _storage; - - constructor(address diamondProxy) { - _storage = IERC6909Storage(diamondProxy); - } - - /** - * @notice Mints tokens for a specific ID. - * @param _id The token ID to mint. - * @param _amount The amount to mint. - * @param _to The recipient address. - */ - function mintToken(uint256 _id, uint256 _amount, address _to) external { - ERC6909Mod.mint(_storage, _id, _amount, _to); - } - - /** - * @notice Transfers tokens between addresses. - * @param _id The token ID to transfer. - * @param _amount The amount to transfer. - * @param _from The sender address. - * @param _to The recipient address. - */ - function transferToken(uint256 _id, uint256 _amount, address _from, address _to) external { - ERC6909Mod.transfer(_storage, _id, _amount, _from, _to); - } -}`} - - -## Best Practices - - -- Ensure the `IERC6909Storage` interface is correctly cast to the diamond proxy address when interacting with the module. -- Handle potential errors from `transfer` and `burn` functions, which may revert due to insufficient balance or invalid operations. -- Be mindful of operator roles when implementing `transfer`; operators bypass allowance checks. - - -## Integration Notes - - -ERC6909Mod relies on the `IERC6909Storage` interface to access its internal state, which is managed in a dedicated storage slot within the diamond proxy. Facets integrating this module must correctly pass the diamond proxy address to the module functions, which will then interact with the shared storage. The `getStorage` function can be used by facets to obtain a direct pointer to the ERC6909 storage struct, allowing for read operations without direct module calls. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx deleted file mode 100644 index 4ee3c69f..00000000 --- a/website/docs/contracts/modules/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,362 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. - - - -- Manages internal state for enumerable ERC-721 tokens, including tracking token ownership and supply. -- Provides atomic operations for minting, transferring, and burning tokens, ensuring consistency across enumeration lists. -- Designed for integration within Compose diamond facets, adhering to the diamond storage pattern. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721EnumerableMod provides essential internal logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures that minted, transferred, and burned tokens are correctly tracked in enumeration lists, maintaining data integrity for token ownership and supply. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod} from "./interfaces/IERC721EnumerableMod.sol"; - -contract MyERC721Facet { - // Assume ERC721EnumerableMod is deployed and its address is known - IERC721EnumerableMod internal immutable erc721EnumerableMod; - - constructor(address _erc721EnumerableModAddress) { - erc721EnumerableMod = IERC721EnumerableMod(_erc721EnumerableModAddress); - } - - /** - * @notice Mints a new ERC-721 token. - * @param _to The address to mint the token to. - * @param _tokenId The ID of the token to mint. - */ - function mintToken(address _to, uint256 _tokenId) external { - // Call the internal mint logic provided by the module - erc721EnumerableMod.mint(_to, _tokenId); - } - - /** - * @notice Transfers an existing ERC-721 token. - * @param _from The address to transfer the token from. - * @param _to The address to transfer the token to. - * @param _tokenId The ID of the token to transfer. - */ - function transferToken(address _from, address _to, uint256 _tokenId) external { - // Call the internal transfer logic provided by the module - erc721EnumerableMod.transferFrom(_from, _to, _tokenId); - } - - /** - * @notice Burns an ERC-721 token. - * @param _tokenId The ID of the token to burn. - */ - function burnToken(uint256 _tokenId) external { - // Call the internal burn logic provided by the module - erc721EnumerableMod.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721EnumerableMod` is correctly initialized and its address is accessible to facets that require its functionality. -- Always validate input parameters for token IDs and recipient addresses before calling module functions to prevent unexpected state changes or reverts. -- Implement robust access control within your facets to ensure only authorized entities can initiate mint, transfer, or burn operations. - - -## Integration Notes - - -The `ERC721EnumerableMod` interacts with a predefined ERC-721 enumerable storage struct within the diamond's storage. The `getStorage` function allows facets to access this struct directly using inline assembly, ensuring efficient retrieval. Changes made by `mint`, `transferFrom`, and `burn` directly update this shared storage, making them immediately visible to all facets interacting with the ERC-721 enumerable state. Facets should not attempt to replicate this storage management logic. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx deleted file mode 100644 index a001a521..00000000 --- a/website/docs/contracts/modules/ERC721Mod.mdx +++ /dev/null @@ -1,359 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. - - - -- Provides core ERC-721 operations: mint, burn, transfer. -- Integrates with diamond storage using a predefined storage slot for ERC-721 state. -- Reverts on invalid operations such as minting an existing token or burning a non-existent token. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod module provides essential internal logic for managing ERC-721 compliant tokens within a Compose diamond. It enables facets to safely mint, transfer, burn, and manage metadata for non-fungible tokens, leveraging the diamond's storage pattern for efficient and composable state management. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod} from "@compose/modules/ERC721Mod.sol"; - -contract MyERC721Facet { - address constant ERC721_STORAGE_SLOT = 0x5f636306d713946618412e127e1079e4c27849b993960d260f01f2e124712e60; // Example slot, replace with actual - - function mintToken(address _to, uint256 _tokenId) external { - IERC721Mod erc721Mod = IERC721Mod(address(this)); // Assuming facet has access to module - erc721Mod.mint(_to, _tokenId); - } - - function transferExistingToken(address _from, address _to, uint256 _tokenId) external { - IERC721Mod erc721Mod = IERC721Mod(address(this)); - erc721Mod.transferFrom(_from, _to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - IERC721Mod erc721Mod = IERC721Mod(address(this)); - erc721Mod.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the ERC721Mod contract is correctly initialized and accessible to facets that require ERC-721 functionality. -- Implement robust access control within your facets to restrict who can call mint, burn, and transfer functions if necessary. -- Handle potential reverts from module functions (e.g., token not existing during burn, zero address during mint) gracefully within your facet logic. - - -## Integration Notes - - -The ERC721Mod interacts with diamond storage at a predefined slot to manage its internal ERC-721 state. Facets must be aware of this storage layout and the specific slot used to ensure correct initialization and data access. The `getStorage` function can be used by facets to retrieve the ERC-721 storage struct directly, allowing for read operations without calling specific management functions. - - -
- -
- - diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx deleted file mode 100644 index 21d09e98..00000000 --- a/website/docs/contracts/modules/NonReentrancyMod.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. - - - -- Prevents reentrant function calls by maintaining a simple lock state. -- Designed for seamless integration into Compose diamond facets using the `using for` directive. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod provides essential utilities to prevent reentrancy attacks within your diamond facets. By integrating this module, you can ensure that sensitive operations are executed atomically, safeguarding your contract's state integrity. - ---- - -## Storage - ---- -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose-protocol/contracts/src/modules/non-reentrancy/LibNonReentrancy.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 private _lock; - - /** - * @notice Example function protected by non-reentrancy. - */ - function protectedAction() external { - // Acquire the lock before executing sensitive logic. - _lock.enter(); - - // ... perform sensitive operations ... - - // Release the lock after operations are complete. - _lock.exit(); - } - - /** - * @notice Another function demonstrating reentrancy protection. - */ - function anotherAction() external { - // Enter the non-reentrancy lock. - _lock.enter(); - - // ... perform operations ... - - // Exit the non-reentrancy lock. - _lock.exit(); - } -}`} - - -## Best Practices - - -- Always pair `enter()` with `exit()` to ensure the lock is released, even in the event of an internal revert. -- Use `_lock.enter()` immediately upon entering a function and `_lock.exit()` just before returning or reverting to maximize protection. -- Integrate this module into facets that handle critical state changes or asset transfers to prevent recursive calls. - - -## Integration Notes - - -The NonReentrancyMod relies on a single `uint256` storage variable to track the reentrancy lock. Facets using this module should declare a `uint256` variable (e.g., `_lock`) and associate it with the `LibNonReentrancy` library using `using LibNonReentrancy for uint256;`. The `enter()` function increments this variable, and `exit()` decrements it. It is crucial that the storage slot for this lock variable is not shared or modified by other facets in a way that could compromise the lock's integrity. The order of operations within a facet is critical: `enter()` must be called before any potentially reentrant calls, and `exit()` must be called after all sensitive operations are completed. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx deleted file mode 100644 index c063506d..00000000 --- a/website/docs/contracts/modules/OwnerMod.mdx +++ /dev/null @@ -1,258 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. - - - -- Provides ERC-173 compliant owner management functions (`owner`, `transferOwnership`, `setContractOwner`). -- Includes a utility function `requireOwner()` for easy access control enforcement. -- Defines a clear storage layout for owner tracking, compatible with the Compose diamond storage pattern. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -OwnerMod provides essential ERC-173 contract ownership management for Compose diamonds. It defines the storage layout and functions required to track and enforce contract ownership, ensuring that critical administrative actions can only be performed by the designated owner. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; -import {OwnerModStorage} from "@compose/modules/owner/OwnerModStorage.sol"; - -contract MyOwnerFacet { - uint256 constant OWNER_STORAGE_SLOT = 1; // Example slot - - function owner() external view returns (address) { - return IOwnerMod(address(this)).owner(); - } - - function transferContractOwnership(address _newOwner) external { - IOwnerMod(address(this)).transferOwnership(_newOwner); - } - - function setOwner(address _newOwner) external { - // Assuming OwnerMod is correctly initialized with the owner - // and this facet has access to the storage. - IOwnerMod(address(this)).setContractOwner(_newOwner); - } - - // Example of protecting a function with owner-only access - function sensitiveAdminAction() external { - IOwnerMod(address(this)).requireOwner(); - // Perform sensitive action - } -}`} - - -## Best Practices - - -- Always use `requireOwner()` before executing any function that modifies critical contract state or performs administrative tasks. -- When transferring ownership, consider the implications of setting the new owner to `address(0)` as this renounces ownership permanently. -- Ensure the `OwnerModStorage` struct is correctly laid out in your diamond's storage, respecting the defined `STORAGE_POSITION`. - - -## Integration Notes - - -The OwnerMod integrates with the diamond's storage by defining a specific storage slot for its `OwnerModStorage` struct. Facets interacting with owner functionalities must use the `IOwnerMod` interface to call the provided functions. The `getStorage` function within OwnerMod (though not directly callable by external facets) uses inline assembly to access this specific storage slot. Changes to the owner are immediately reflected and enforced by subsequent calls to `requireOwner()`. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx deleted file mode 100644 index ece21660..00000000 --- a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,307 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. - - - -- Two-step ownership transfer for enhanced security. -- `renounceOwnership` function to set owner to `address(0)`. -- `requireOwner` modifier-like functionality for access control. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerTwoStepsMod provides a secure, two-step ownership transfer mechanism for your diamond. This pattern prevents accidental ownership loss by requiring explicit acceptance from the new owner, enhancing contract safety and auditability. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -Storage position: `OWNER_STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {OwnerTwoStepsMod} from "@compose/modules/ownership/OwnerTwoStepsMod.sol"; - -contract MyFacet { - OwnerTwoStepsMod private ownerMod; - - constructor(address ownerTwoStepsModAddress) { - ownerMod = OwnerTwoStepsMod(ownerTwoStepsModAddress); - } - - function transferContractOwnership(address _newOwner) external { - ownerMod.transferOwnership(_newOwner); - } - - function acceptContractOwnership() external { - ownerMod.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return ownerMod.owner(); - } - - function getPendingContractOwner() external view returns (address) { - return ownerMod.pendingOwner(); - } - - function protectAdminFunction() external { - ownerMod.requireOwner(); - // ... admin logic ... - } -}`} - - -## Best Practices - - -- Always use `transferOwnership` followed by `acceptOwnership` for ownership changes to prevent accidental lockouts. -- Ensure the `OwnerTwoStepsMod` contract is deployed and accessible by your facets. -- Consider gas implications for users when designing ownership transfer workflows. - - -## Integration Notes - - -The OwnerTwoStepsMod relies on specific storage slots for its `Owner` and `PendingOwner` state variables, defined by `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` respectively. Facets interacting with this module should be aware that these storage slots are managed by the module. Changes to these storage slots outside of the module's functions will lead to unpredictable behavior. The module provides `getOwnerStorage` and `getPendingOwnerStorage` for direct access if necessary, but direct manipulation is discouraged. - - -
- -
- - diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx deleted file mode 100644 index 1d562d25..00000000 --- a/website/docs/contracts/modules/RoyaltyMod.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic." -gitSource: "https://github.com/maxnorm/Compose/blob/4f58223f08f652d9b72a6792643c40cac4f0f4a0/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic. - - - -- Implements ERC-2981 standard for royalty payments. -- Supports both default and token-specific royalty configurations. -- Provides functions to set, reset, and query royalty information efficiently. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The RoyaltyMod module provides an implementation of the ERC-2981 royalty standard, enabling diamonds to manage and query royalty information for tokens. It supports both default royalties applicable to all tokens and token-specific overrides, ensuring compliance with royalty distribution requirements. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -Storage position: `STORAGE_POSITION` - Used for diamond storage pattern. ---- -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "../interfaces/IRoyaltyMod.sol"; -import {IDiamondStorage} from "../interfaces/IDiamondStorage.sol"; - -contract RoyaltyFacet { - // Assume IDiamondStorage is available and initialized - IDiamondStorage internal _diamondStorage; - - constructor(address diamondAddress) { - _diamondStorage = IDiamondStorage(diamondAddress); - } - - /** - * @notice Sets royalty information for a specific token. - * @param _tokenId The ID of the token. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee in basis points. - */ - function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - // Access the RoyaltyMod internal functions via the diamond storage interface - IRoyaltyMod(address(_diamondStorage.getRoyaltyMod())).setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); - } - - /** - * @notice Queries royalty information for a given token and sale price. - * @param _tokenId The ID of the token. - * @param _salePrice The sale price of the token. - * @return _receiver The address to receive royalties. - * @return _feeBasisPoints The royalty fee in basis points. - */ - function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address _receiver, uint256 _feeBasisPoints) { - // Access the RoyaltyMod internal functions via the diamond storage interface - return IRoyaltyMod(address(_diamondStorage.getRoyaltyMod())).royaltyInfo(_tokenId, _salePrice); - } -}`} - - -## Best Practices - - -- Ensure the `_receiver` address is validated for safety and intent before setting royalties. -- Use `deleteDefaultRoyalty` or `resetTokenRoyalty` to clear royalty information when no longer applicable, adhering to gas efficiency principles. -- Be aware that `royaltyInfo` queries will fallback to default royalties if no token-specific royalty is set. - - -## Integration Notes - - -The RoyaltyMod is designed to be integrated via a diamond proxy. Facets interacting with royalty logic should call the `IRoyaltyMod` interface, which is expected to be accessible through the diamond's storage mechanism (e.g., `_diamondStorage.getRoyaltyMod()`). The `royaltyInfo` function queries token-specific royalties first and falls back to default royalties if none are found for the given token. Storage for default royalties is managed in a predefined slot, while token-specific royalties are stored in a mapping accessible via the module. - - -
- -
- - From 990e38dd6f5fc9e5db5c8887a2bcb8c071280959 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sat, 20 Dec 2025 02:05:52 +0000 Subject: [PATCH 38/68] docs: auto-generate docs pages from NatSpec --- .../contracts/facets/AccessControlFacet.mdx | 547 +++++++++++++ .../facets/AccessControlPausableFacet.mdx | 375 +++++++++ .../facets/AccessControlTemporalFacet.mdx | 467 +++++++++++ .../docs/contracts/facets/DiamondCutFacet.mdx | 371 +++++++++ .../contracts/facets/DiamondLoupeFacet.mdx | 255 ++++++ .../docs/contracts/facets/ERC1155Facet.mdx | 699 ++++++++++++++++ .../contracts/facets/ERC20BridgeableFacet.mdx | 434 ++++++++++ .../docs/contracts/facets/ERC20BurnFacet.mdx | 252 ++++++ website/docs/contracts/facets/ERC20Facet.mdx | 578 ++++++++++++++ .../contracts/facets/ERC20PermitFacet.mdx | 334 ++++++++ .../docs/contracts/facets/ERC6909Facet.mdx | 530 ++++++++++++ .../docs/contracts/facets/ERC721BurnFacet.mdx | 209 +++++ .../facets/ERC721EnumerableBurnFacet.mdx | 222 ++++++ .../facets/ERC721EnumerableFacet.mdx | 753 ++++++++++++++++++ website/docs/contracts/facets/ERC721Facet.mdx | 663 +++++++++++++++ .../docs/contracts/facets/ExampleDiamond.mdx | 129 +++ website/docs/contracts/facets/OwnerFacet.mdx | 216 +++++ .../contracts/facets/OwnerTwoStepsFacet.mdx | 292 +++++++ .../docs/contracts/facets/RoyaltyFacet.mdx | 195 +++++ .../contracts/modules/AccessControlMod.mdx | 450 +++++++++++ .../modules/AccessControlPausableMod.mdx | 388 +++++++++ .../modules/AccessControlTemporalMod.mdx | 479 +++++++++++ .../docs/contracts/modules/DiamondCutMod.mdx | 414 ++++++++++ website/docs/contracts/modules/DiamondMod.mdx | 234 ++++++ website/docs/contracts/modules/ERC1155Mod.mdx | 618 ++++++++++++++ website/docs/contracts/modules/ERC165Mod.mdx | 155 ++++ .../contracts/modules/ERC20BridgeableMod.mdx | 424 ++++++++++ website/docs/contracts/modules/ERC20Mod.mdx | 424 ++++++++++ .../docs/contracts/modules/ERC20PermitMod.mdx | 282 +++++++ website/docs/contracts/modules/ERC6909Mod.mdx | 528 ++++++++++++ .../contracts/modules/ERC721EnumerableMod.mdx | 347 ++++++++ website/docs/contracts/modules/ERC721Mod.mdx | 354 ++++++++ .../contracts/modules/NonReentrancyMod.mdx | 142 ++++ website/docs/contracts/modules/OwnerMod.mdx | 253 ++++++ .../contracts/modules/OwnerTwoStepsMod.mdx | 318 ++++++++ website/docs/contracts/modules/RoyaltyMod.mdx | 364 +++++++++ 36 files changed, 13695 insertions(+) create mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx create mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx create mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx create mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC20Facet.mdx create mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx create mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx create mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx create mode 100644 website/docs/contracts/facets/ERC721Facet.mdx create mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx create mode 100644 website/docs/contracts/facets/OwnerFacet.mdx create mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx create mode 100644 website/docs/contracts/modules/AccessControlMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx create mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx create mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx create mode 100644 website/docs/contracts/modules/DiamondMod.mdx create mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx create mode 100644 website/docs/contracts/modules/ERC165Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx create mode 100644 website/docs/contracts/modules/ERC20Mod.mdx create mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx create mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx create mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx create mode 100644 website/docs/contracts/modules/ERC721Mod.mdx create mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx create mode 100644 website/docs/contracts/modules/OwnerMod.mdx create mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx create mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx new file mode 100644 index 00000000..bf82719a --- /dev/null +++ b/website/docs/contracts/facets/AccessControlFacet.mdx @@ -0,0 +1,547 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Role-based access control (RBAC) facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control (RBAC) facet for Compose diamonds + + + +- Supports standard RBAC patterns with predefined roles like `DEFAULT_ADMIN_ROLE`. +- Provides granular control over granting and revoking roles for individual accounts and in batches. +- Includes a `requireRole` function for easy inline access control checks within other facets. + + +## Overview + +The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling diamond administrators to grant, revoke, and manage roles for various accounts, ensuring secure and controlled access to diamond functionalities. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondProxy, DiamondInit} from "@compose/diamond-proxy/DiamondProxy.sol"; +import {AccessControlFacet} from "@compose/access-control/AccessControlFacet.sol"; + +contract MyDiamondInit is DiamondInit { + function init() public { + // ... other initializations ... + AccessControlFacet accessControlFacet = AccessControlFacet(address(this)); + bytes32 adminRole = accessControlFacet.getRoleAdmin(keccak256("DEFAULT_ADMIN_ROLE")); + accessControlFacet.grantRole(adminRole, msg.sender); + } +} + +contract MyDiamond is DiamondProxy { + function upgradeTo(address newImplementation) public { + // ... upgrade logic ... + } + + function execute() public { + AccessControlFacet accessControlFacet = AccessControlFacet(address(this)); + bytes32 MY_ROLE = keccak256("MY_ROLE"); + accessControlFacet.grantRole(MY_ROLE, tx.origin); + accessControlFacet.requireRole(MY_ROLE, tx.origin); + // ... execute protected function ... + } +}`} + + +## Best Practices + + +- Initialize roles and grant initial administrative privileges during diamond deployment using `DiamondInit`. +- Use `getRoleAdmin` and `setRoleAdmin` to manage role hierarchies and administrative delegation effectively. +- Leverage batch functions (`grantRoleBatch`, `revokeRoleBatch`) for efficient management of multiple accounts for a single role. + + +## Security Considerations + + +Ensure that the caller is authorized to manage roles by checking the role admin before calling `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch`. The `renounceRole` function should be used with caution as it permanently removes the caller's role. Input validation is critical; ensure that roles and account addresses are correctly formatted before being passed to functions. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..e5155bf6 --- /dev/null +++ b/website/docs/contracts/facets/AccessControlPausableFacet.mdx @@ -0,0 +1,375 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Role-based access control with pause functionality for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control with pause functionality for Compose diamonds + + + +- Granular role pausing: Allows selective disabling of functionalities tied to specific roles. +- Admin-controlled pausing: Only designated role administrators can pause or unpause roles. +- Integrated with role-based access control: Seamlessly combines pausing with existing role checks. + + +## Overview + +The AccessControlPausableFacet enhances a Compose diamond by integrating role-based access control with the ability to pause specific roles. This allows for granular control over functionality, enabling administrators to temporarily halt operations for certain roles without affecting the entire diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose/diamond-loupe/DiamondLoupeFacet.sol"; +import {DiamondCutFacet} from "@compose/diamond-cut/DiamondCutFacet.sol"; +import {AccessControlPausableFacet} from "@compose/access-control/AccessControlPausableFacet.sol"; + +contract DeployDiamond { + // ... deployment setup ... + + function deployDiamond() public { + // ... other facet deployments ... + + AccessControlPausableFacet accessControlPausableFacet = new AccessControlPausableFacet(); + + // Add AccessControlPausableFacet to the diamond + // ... Diamond Cut operations ... + + // Example: Get storage pointers + AccessControlPausableFacet.AccessControlPausableStorage storage acpStorage = AccessControlPausableFacet.getStorage(); + // AccessControlPausableFacet.AccessControlStorage storage acStorage = AccessControlPausableFacet.getAccessControlStorage(); + + // Example: Pause a role (assuming 'MY_ROLE' is defined and caller is admin) + // accessControlPausableFacet.pauseRole(keccak256("MY_ROLE")); + + // Example: Check if a role is paused + // bool isPaused = accessControlPausableFacet.isRolePaused(keccak256("MY_ROLE")); + } +}`} + + +## Best Practices + + +- Initialize roles and assign administrators via an upgrade function or during deployment to ensure proper access control setup. +- Use `requireRoleNotPaused` within your facet functions to enforce the paused state of roles before executing sensitive operations. +- Store role identifiers consistently (e.g., using `keccak256` hashes of role names) to avoid discrepancies when checking pause status or granting permissions. + + +## Security Considerations + + +Ensure that the caller attempting to pause or unpause a role possesses the necessary administrative privileges for that specific role. Reentrancy is not a direct concern for `pauseRole` and `unpauseRole` as they only modify internal state and do not make external calls. However, downstream functions that check the paused state must be audited for reentrancy risks. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..e70c115e --- /dev/null +++ b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx @@ -0,0 +1,467 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Time-limited role-based access control for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Time-limited role-based access control for Compose diamonds + + + +- Time-limited role assignments with automatic expiry. +- Granular control over temporary access permissions. +- Integration with Compose's standard role-based access control system. + + +## Overview + +The AccessControlTemporalFacet extends Compose's access control with time-limited role assignments. It allows roles to be granted for a specific duration, automatically expiring after the set timestamp. This facet is crucial for managing temporary permissions within a diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose/diamond-contracts/facets/DiamondLoupeFacet.sol"; +import {AccessControlFacet} from "@compose/diamond-contracts/facets/AccessControlFacet.sol"; +import {AccessControlTemporalFacet} from "@compose/diamond-contracts/facets/AccessControlTemporalFacet.sol"; + +contract DeployDiamondExample { + // Assume diamond and facets are already deployed and selectors registered + // For example purposes, we'll interact with the facets directly. + address diamondAddress; // Address of your deployed diamond + + function grantTempRole() public { + AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(diamondAddress); + address user = address(1); // Example user address + bytes32 role = AccessControlFacet.DEFAULT_ADMIN_ROLE(); // Example role + uint256 expiryTimestamp = block.timestamp + 3600; // Role expires in 1 hour + + // Caller must be the admin of the role to grant it with expiry + temporalFacet.grantRoleWithExpiry(role, user, expiryTimestamp); + } + + function checkRole() public view returns (bool) { + AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(diamondAddress); + address user = address(1); // Example user address + bytes32 role = AccessControlFacet.DEFAULT_ADMIN_ROLE(); // Example role + + // This will revert if the role is expired or not held + try temporalFacet.requireValidRole(role, user) { + return true; + } catch (bytes memory) { + return false; + } + } + + function revokeTempRole() public { + AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(diamondAddress); + address user = address(1); // Example user address + bytes32 role = AccessControlFacet.DEFAULT_ADMIN_ROLE(); // Example role + + // Caller must be the admin of the role to revoke it + temporalFacet.revokeTemporalRole(role, user); + } +}`} + + +## Best Practices + + +- Initialize the `AccessControlFacet` before using `AccessControlTemporalFacet` to set up roles and administrators. +- Ensure that the caller attempting to grant or revoke temporal roles is the designated admin for that specific role. +- Regularly monitor role expiry to ensure timely revocation of temporary permissions, especially for sensitive operations. + + +## Security Considerations + + +The `grantRoleWithExpiry` and `revokeTemporalRole` functions are restricted to the admin of the role, preventing unauthorized role manipulation. The `requireValidRole` function includes checks for both role existence and expiry, reverting if the role is invalid or expired, mitigating risks associated with stale permissions. Reentrancy is not a concern as these functions do not make external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx new file mode 100644 index 00000000..fe0f31af --- /dev/null +++ b/website/docs/contracts/facets/DiamondCutFacet.mdx @@ -0,0 +1,371 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Diamond upgrade (cut) facet for ERC-2535 diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond upgrade (cut) facet for ERC-2535 diamonds + + + +- Self-contained facet with no imports or inheritance +- Only `external` and `internal` function visibility +- Follows Compose readability-first conventions +- Ready for diamond integration + + +## Overview + +Diamond upgrade (cut) facet for ERC-2535 diamonds + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +
+ +
+ + diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..7825e4c6 --- /dev/null +++ b/website/docs/contracts/facets/DiamondLoupeFacet.mdx @@ -0,0 +1,255 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "The functions in DiamondLoupeFacet MUST be added to a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +The functions in DiamondLoupeFacet MUST be added to a diamond. + + + +- Provides comprehensive introspection of diamond facets and their associated function selectors. +- Offers efficient querying of facet addresses based on function selectors. +- Optimized for performance on diamonds with numerous facets and selectors, utilizing memory-efficient hashing techniques. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond proxy. It allows developers to query which facets are registered, the selectors they support, and the addresses of these facets. This facet is crucial for understanding the diamond's internal structure and for building external tooling or logic that interacts with specific diamond functionalities. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose-protocol/diamond/contracts/facets/DiamondLoupeFacet.sol"; +import {IDiamondLoupe} from "@compose-protocol/diamond/contracts/interfaces/IDiamondLoupe.sol"; + +contract DiamondLoupeConsumer { + IDiamondLoupe public diamondLoupe; + + constructor(address _diamondAddress) { + diamondLoupe = IDiamondLoupe(_diamondAddress); + } + + function getAllFacets() public view returns (IDiamondLoupe.Facet[] memory) { + return diamondLoupe.facets(); + } + + function getFacetAddr(bytes4 _selector) public view returns (address) { + return diamondLoupe.facetAddress(_selector); + } + + function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { + return diamondLoupe.facetFunctionSelectors(_facet); + } +}`} + + +## Best Practices + + +- Initialize the `DiamondLoupeFacet` during diamond deployment to ensure introspection functions are available from the start. +- Access `DiamondLoupeFacet` functions via the diamond proxy address to ensure all calls are routed correctly and adhere to diamond proxy logic. +- When querying for facet information, be mindful of gas costs, especially with `facets()` on diamonds with a very large number of facets and selectors; consider using more specific functions like `facetAddress()` or `facetFunctionSelectors()` when possible. + + +## Security Considerations + + +The `DiamondLoupeFacet` is generally read-only and does not modify state, thus posing minimal direct security risks. However, the information it provides can be critical for security audits and for understanding access control mechanisms implemented by other facets. Ensure that the diamond's upgradeability is managed securely, as changes to facet registration or function selector mappings could impact the integrity of the information returned by this facet. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx new file mode 100644 index 00000000..4c2b71e6 --- /dev/null +++ b/website/docs/contracts/facets/ERC1155Facet.mdx @@ -0,0 +1,699 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "ERC-1155 multi-token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-1155 multi-token facet for Compose diamonds + + + +- Implements the ERC-1155 Multi-Token Standard, supporting both fungible and non-fungible tokens. +- Provides batched transfer and balance checking functions (`balanceOfBatch`, `safeBatchTransferFrom`) for efficiency. +- Supports customizable token URIs, allowing for dynamic metadata based on token ID and an optional base URI. + + +## Overview + +The ERC1155Facet implements the ERC-1155 multi-token standard for Compose diamonds. It provides core functionalities for managing and transferring fungible and non-fungible tokens within the diamond's composable architecture. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut, IERC1155Facet} from \"@compose/contracts/src/interfaces/IDiamond.sol\"; + +contract ERC1155Deployer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function deployERC1155Facet(address _diamondCutAddress) external { + // Assume ERC1155Facet contract is deployed and its address is known + address erc1155FacetAddress = address(0xYourERC1155FacetAddress); + + // Define the functions to be added by this facet + bytes4[] memory selectors = new bytes4[](8); + selectors[0] = IERC1155Facet.getStorage.selector; + selectors[1] = IERC1155Facet.uri.selector; + selectors[2] = IERC1155Facet.balanceOf.selector; + selectors[3] = IERC1155Facet.balanceOfBatch.selector; + selectors[4] = IERC1155Facet.setApprovalForAll.selector; + selectors[5] = IERC1155Facet.isApprovedForAll.selector; + selectors[6] = IERC1155Facet.safeTransferFrom.selector; + selectors[7] = IERC1155Facet.safeBatchTransferFrom.selector; + + // Prepare the DiamondCut data + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: erc1155FacetAddress, + action: IDiamondCut.FacetCutAction.ADD, + functionSelectors: selectors + }); + + // Execute the diamond cut to add the ERC1155 facet + bytes memory data = abi.encodeWithSelector(IDiamondCut.diamondCut.selector, cuts); + // Assuming IDiamondCut interface is accessible and the diamond supports the cut + // The actual call would be to the diamond proxy's fallback or delegatecall mechanism + // For simplicity, this example shows the intended data structure. + // In a real deployment, you'd call the diamond proxy directly with this data. + // Example: _diamondCutAddress.diamondCut(cuts); + } + + // Example of calling functions on the ERC1155 facet via the diamond proxy + function callERC1155Functions() external { + // Assuming IERC1155Facet interface is available and diamondAddress is set + IERC1155Facet erc1155 = IERC1155Facet(diamondAddress); + + // Example: Get balance of token ID 1 for address(this) + uint256 balance = erc1155.balanceOf(address(this), 1); + + // Example: Get URI for token ID 2 + string memory tokenUri = erc1155.uri(2); + } +}`} + + +## Best Practices + + +- Initialize the ERC1155 facet with necessary configuration, such as setting a base URI if token-specific URIs will be used for concatenation. Ensure the diamond's storage slot for ERC1155 is correctly set during deployment or upgrades. +- When upgrading, ensure that the function selectors for the ERC1155 facet are correctly managed to maintain compatibility and prevent accidental removal of critical functions. +- Carefully manage access control for functions that modify state (e.g., `setApprovalForAll`) by integrating with the diamond's access control mechanisms, if applicable, to restrict sensitive operations. + + +## Security Considerations + + +The `safeTransferFrom` and `safeBatchTransferFrom` functions must be called with valid parameters to prevent unintended token transfers or loss. Ensure that the caller has sufficient allowance or ownership of the tokens being transferred. The `setApprovalForAll` function grants broad permissions to an operator, so callers should exercise caution when approving addresses. Reentrancy is not an explicit concern within the facet's core transfer logic as it relies on standard ERC-1155 patterns, but external contract interactions initiated by the diamond proxy should be audited for reentrancy risks. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..0dd55c0c --- /dev/null +++ b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx @@ -0,0 +1,434 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "ERC-20 token bridgeable facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token bridgeable facet for Compose diamonds + + + +- Enables secure cross-chain minting and burning of ERC-20 tokens. +- Enforces `trusted-bridge` role for all cross-chain mint/burn operations. +- Provides internal helper functions to access facet storage safely. + + +## Overview + +The ERC20BridgeableFacet enables cross-chain interoperability for ERC-20 tokens within a Compose diamond. It provides functions for minting and burning tokens on behalf of trusted bridges, ensuring secure and controlled cross-chain token transfers. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "../facets/ERC20BridgeableFacet.sol"; +import {IDiamondCut} from "@compose/diamond-proxy/contracts/interfaces/IDiamondCut.sol"; + +contract DeployERC20BridgeableFacet { + address diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function deploy() public { + // Assume ERC20BridgeableFacet is already compiled and its bytecode is available + bytes memory erc20BridgeableFacetBytecode = type(ERC20BridgeableFacet).creation.runtime.bytecode; + + IDiamondCut(diamondAddress).diamondCut( + IDiamondCut.FacetCut[] memory setAction, + address(0), // clear previous diamond admin + bytes('') // init data + ); + + // Add the ERC20BridgeableFacet + IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](1); + facetCuts[0] = IDiamondCut.FacetCut( + ERC20BridgeableFacet(address(0)).crosschainMint.selector, + ERC20BridgeableFacet(address(0)).crosschainMint.selector, + erc20BridgeableFacetBytecode + ); + + IDiamondCut(diamondAddress).diamondCut( + facetCuts, + address(0), // clear previous diamond admin + bytes('') // init data + ); + } + + function exampleCrosschainMint(address _token, address _to, uint256 _amount) public { + IERC20BridgeableFacet(diamondAddress).crosschainMint(_token, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is correctly assigned in the AccessControl facet for authorized cross-chain operations. +- Initialize the diamond with this facet by including it in the initial `diamondCut` or a subsequent upgrade transaction. +- Use `getERC20Storage` and `getAccessControlStorage` to safely access facet-specific storage within your custom facets or logic. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role. Ensure this role is granted only to verified and secure bridge operators. The `checkTokenBridge` internal function prevents unauthorized calls by verifying the caller's role and ensuring the caller is not the zero address. Reentrancy is not a direct concern for mint/burn operations, but ensure any custom logic interacting with this facet is reentrancy-safe. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx new file mode 100644 index 00000000..ed741d6a --- /dev/null +++ b/website/docs/contracts/facets/ERC20BurnFacet.mdx @@ -0,0 +1,252 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "ERC-20 token burn facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token burn facet for Compose diamonds + + + +- Supports burning tokens directly from a user's balance. +- Enables burning tokens from another account using pre-approved allowances. +- Emits `Transfer` events to the zero address, adhering to ERC-20 burn event standards. + + +## Overview + +The ERC20BurnFacet enables the destruction of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account via allowance, facilitating token supply management and deflationary mechanics. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BurnFacet} from "@compose/diamond/facets/ERC20/ERC20BurnFacet.sol"; + +contract ERC20BurnConsumer { + address immutable _diamondAddress; + + constructor(address diamondAddress) { + _diamondAddress = diamondAddress; + } + + function consumeBurn() external { + IERC20BurnFacet burnFacet = IERC20BurnFacet(_diamondAddress); + // Example: Burn 100 tokens from the caller's balance + burnFacet.burn(100); + } + + function consumeBurnFrom(address _from, uint256 _amount) external { + IERC20BurnFacet burnFacet = IERC20BurnFacet(_diamondAddress); + // Example: Burn 50 tokens from _from's balance, assuming caller has allowance + burnFacet.burnFrom(_from, _amount); + } +}`} + + +## Best Practices + + +- Initialize the diamond with this facet to enable burning capabilities. +- Ensure appropriate access control is set for `burnFrom` if restricted burning is desired. +- Understand that `burn` and `burnFrom` reduce the total token supply. + + +## Security Considerations + + +The `burn` function is permissionless and operates on the caller's balance. The `burnFrom` function requires the caller to have an allowance set for the tokens being burned. No reentrancy concerns exist as these functions do not make external calls. Input validation ensures non-zero amounts are burned. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx new file mode 100644 index 00000000..13b34f27 --- /dev/null +++ b/website/docs/contracts/facets/ERC20Facet.mdx @@ -0,0 +1,578 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "ERC-20 fungible token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 fungible token facet for Compose diamonds + + + +- Implements the full ERC-20 standard interface for fungible tokens. +- Integrates seamlessly into the Compose diamond architecture, allowing composability with other facets. +- Provides direct access to token state variables via `getStorage` for off-chain or internal tooling. + + +## Overview + +The ERC20Facet provides standard ERC-20 fungible token functionality within a Compose diamond. It manages token metadata, supply, balances, and allowances, enabling seamless integration with other diamond facets and external applications. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {ERC20Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20Facet.sol"; + +contract DeployERC20Diamond { + // Assume DiamondInit and DiamondLoupeFacet are deployed and initialized + address internal diamondProxyAddress; + + function deploy() public { + // ... deployment logic for DiamondInit and DiamondLoupeFacet ... + + // Deploy ERC20Facet + ERC20Facet erc20Facet = new ERC20Facet(); + + // Prepare facet cut for ERC20Facet + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: address(erc20Facet), + action: IDiamondCut.FacetCutAction.ADD, + selectors: ERC20Facet.getSelectors() + }); + + // Add ERC20Facet to the diamond + // Assumes diamondProxyAddress is already set and initialized + // diamond.diamondCut(cut, address(0), ""); // Replace with actual diamond instance and method + + // Interact with the ERC20 token + // Assumes diamondProxyAddress is the address of your ERC20 token diamond + // ERC20Facet erc20Token = ERC20Facet(diamondProxyAddress); + // uint256 tokenSupply = erc20Token.totalSupply(); + // address tokenOwner = msg.sender; + // erc20Token.transfer(tokenOwner, 100 * 10**18); + } +}`} + + +## Best Practices + + +- Initialize ERC20 token metadata (name, symbol, decimals) via the DiamondInit facet during diamond deployment. +- Leverage the diamond's access control mechanisms for functions like `approve` and `transferFrom` if restricted ownership or permissions are required. +- When upgrading the ERC20Facet, ensure the storage layout remains compatible or use versioned storage patterns to avoid data corruption. + + +## Security Considerations + + +Ensure proper access control is implemented in the diamond's upgrade mechanism to prevent unauthorized facet replacements. Input validation for `transfer` and `transferFrom` functions (e.g., checking for zero addresses and sufficient balances) is crucial. The `approve` function can be susceptible to front-running if not handled carefully in conjunction with diamond-level transaction ordering or meta-transaction relays. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx new file mode 100644 index 00000000..20b374e1 --- /dev/null +++ b/website/docs/contracts/facets/ERC20PermitFacet.mdx @@ -0,0 +1,334 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "ERC-20 token permit facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token permit facet for Compose diamonds + + + +- Implements EIP-2612 for gasless token approvals via signatures. +- Provides `nonces` and `DOMAIN_SEPARATOR` for signature generation and validation. +- Allows setting token allowances without requiring a separate `approve` transaction. + + +## Overview + +The ERC20PermitFacet enables EIP-2612 compliant token transfers within a Compose diamond. It provides the necessary functions to manage nonces, retrieve the domain separator, and execute token allowances via signed messages, enhancing user experience by removing the need for separate approval transactions. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20PermitFacet} from "@compose/facets/ERC20PermitFacet.sol"; +import {DiamondProxy, DiamondInit} from "@compose/core/"; + +contract MyDiamondInit is DiamondInit { + function init() public override { + // ... other initializations ... + ERC20PermitFacet.addFacet(msg.sender); + } +} + +contract MyDiamond is DiamondProxy { + using ERC20PermitFacet for ERC20PermitFacet; + + function getERC20PermitFacet() public pure returns (ERC20PermitFacet) { + return ERC20PermitFacet(address(this)); + } + + // Example of calling permit + function permitToken(address owner, address spender, uint256 value, uint256 deadline, bytes calldata signature) external { + // Assuming the ERC20 token contract is accessible via the diamond + // You would typically have an ERC20Facet or similar to interact with the token + // For demonstration, we call permit directly on the facet instance + getERC20PermitFacet().permit(owner, spender, value, deadline, signature); + } +}`} + + +## Best Practices + + +- Initialize the `ERC20PermitFacet` during diamond deployment using an `IDiamondInit` contract. +- Ensure the underlying ERC-20 token contract is correctly configured and accessible through the diamond's routing. +- Implement robust signature verification logic in off-chain or consumer contracts before submitting permit calls to the diamond. + + +## Security Considerations + + +The `permit` function is susceptible to replay attacks if the `DOMAIN_SEPARATOR` is not properly constructed or if signatures are reused across different chains or diamond instances. Ensure the `deadline` parameter is used to limit the validity of permits. The `nonces` must be managed correctly to prevent signature reuse. Access control for calling `permit` should be considered based on the diamond's overall authorization strategy. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx new file mode 100644 index 00000000..5f3cf847 --- /dev/null +++ b/website/docs/contracts/facets/ERC6909Facet.mdx @@ -0,0 +1,530 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "ERC-6909 minimal multi-token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-6909 minimal multi-token facet for Compose diamonds + + + +- Implements the ERC-6909 minimal multi-token standard. +- Supports individual token balances and allowances per token ID. +- Enables operator approvals for delegated token management. + + +## Overview + +The ERC6909Facet implements the minimal multi-token standard for Compose diamonds. It provides essential functions for managing token balances, allowances, and operator approvals, enabling flexible token interactions within the diamond proxy architecture. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909, ERC6909Facet} from "@compose/contracts/facets/ERC6909/ERC6909Facet.sol"; + +// Assume DiamondInit and DiamondCut are deployed and configured + +contract ERC6909User { + address internal diamondProxy; + + function initialize(address _diamondProxy) public { + diamondProxy = _diamondProxy; + } + + function getUserBalance(address _user, uint256 _id) public view returns (uint256) { + // Call the ERC6909Facet's balanceOf function via the diamond proxy + return ERC6909Facet(diamondProxy).balanceOf(_user, _id); + } + + function transferTokens(address _to, uint256 _id, uint256 _amount) public { + // Call the ERC6909Facet's transfer function via the diamond proxy + ERC6909Facet(diamondProxy).transfer(_to, _id, _amount); + } + + function approveSpender(address _spender, uint256 _id, uint256 _amount) public { + // Call the ERC6909Facet's approve function via the diamond proxy + ERC6909Facet(diamondProxy).approve(_spender, _id, _amount); + } +}`} + + +## Best Practices + + +- Initialize the ERC6909Facet with the correct storage slot during diamond deployment. +- Use the `approve` function to grant allowances before calling `transferFrom`. +- Leverage `setOperator` for managing trusted third-party token management. + + +## Security Considerations + + +Ensure proper access control is implemented at the diamond proxy level for sensitive functions like `transferFrom` and `approve` if they are restricted. Review operator approvals regularly to prevent unauthorized token movements. Input validation for token IDs, amounts, and addresses should be handled carefully to prevent unexpected behavior. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx new file mode 100644 index 00000000..249d1720 --- /dev/null +++ b/website/docs/contracts/facets/ERC721BurnFacet.mdx @@ -0,0 +1,209 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "ERC-721 NFT burn facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 NFT burn facet for Compose diamonds + + + +- Enables the destruction of ERC-721 tokens, permanently removing them from the contract's state. +- Integrates seamlessly with the Compose diamond pattern for modular NFT functionality. +- Adheres to ERC-721 token burning semantics, including removal from enumeration tracking. + + +## Overview + +The ERC721BurnFacet provides the functionality to burn (destroy) ERC-721 non-fungible tokens within a Compose diamond. It allows for the permanent removal of tokens from circulation and enumeration tracking, adhering to ERC-721 standards. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "@compose/facets/erc721/ERC721BurnFacet.sol"; +import {DiamondProxy} from "@compose/core/DiamondProxy.sol"; + +contract ERC721BurnConsumer is DiamondProxy { + IERC721BurnFacet public erc721BurnFacet; + + function setErc721BurnFacet(address _facetAddress) external onlyOwner { + erc721BurnFacet = IERC721BurnFacet(_facetAddress); + } + + function burnToken(uint256 _tokenId) external { + // Ensure the caller has ownership or is approved + // ... access control logic here ... + erc721BurnFacet.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Initialize the `ERC721BurnFacet` via the diamond upgrade mechanism, ensuring it is correctly registered and accessible. +- Implement robust access control within your diamond's logic layer (e.g., an access control facet) to determine who can call the `burn` function for a given token. +- Ensure the underlying ERC721 storage layout is compatible with the `STORAGE_POSITION` defined within the `ERC721BurnFacet`. + + +## Security Considerations + + +The `burn` function should only be callable by the token owner or an address approved on their behalf. Implement appropriate access control checks before invoking `ERC721BurnFacet.burn`. The facet directly manipulates storage, so ensure correct initialization and that no other facets are attempting to write to the same storage slot concurrently. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..a87a6448 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,222 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "ERC-721 NFT enumerableburn facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 NFT enumerableburn facet for Compose diamonds + + + +- Enables the burning of ERC721 tokens, effectively destroying them. +- Integrates with enumeration logic, ensuring burned tokens are removed from tracking. +- Supports the Compose diamond pattern for modularity and upgradeability. + + +## Overview + +The ERC721EnumerableBurnFacet extends ERC721 functionality by enabling the burning of NFTs. It integrates with the diamond's storage pattern to ensure token destruction is correctly reflected in enumeration tracking, maintaining the integrity of the NFT collection. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurn } from "../interfaces/IERC721EnumerableBurn.sol"; +import { Diamond } from "@openzeppelin/contracts/diamond/Diamond.sol"; + +contract ERC721EnumerableBurnDeployer { + address internal diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnToken(uint256 _tokenId) external { + IERC721EnumerableBurn(diamondAddress).burn(_tokenId); + } +}`} + + +## Best Practices + + +- Initialize the facet's storage correctly during diamond deployment to ensure proper enumeration tracking. +- Ensure the `burn` function is called with valid token IDs that exist within the contract's state. +- Understand that burning a token removes it permanently and affects the total token count and enumeration order. + + +## Security Considerations + + +The `burn` function requires careful access control to prevent unauthorized token destruction. Ensure that only the token owner or an authorized entity can call this function. Reentrancy is not a concern as the function does not make external calls. Input validation on `_tokenId` is crucial to prevent errors when attempting to burn non-existent tokens. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..1d9d5615 --- /dev/null +++ b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx @@ -0,0 +1,753 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "ERC-721 NFT enumerable facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 NFT enumerable facet for Compose diamonds + + + +- Full ERC721 compliance with enumerable extensions, allowing iteration over all tokens and tokens owned by an address. +- Supports both direct and safe transfers, including checks for receiver contract compatibility. +- Provides essential NFT metadata functions like `name`, `symbol`, and `tokenURI`. +- Manages token ownership, supply, and approval states. + + +## Overview + +The ERC721EnumerableFacet provides a complete implementation of the ERC721 non-fungible token standard, including enumerable extensions. It handles token metadata, ownership tracking, approvals, and transfers, enabling a Compose diamond to act as a robust NFT collection. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondProxy, DiamondInit} from "@compose-protocol/diamond-proxy/contracts/DiamondProxy.sol"; +import {DiamondCut} from "@compose-protocol/diamond-proxy/contracts/DiamondCut.sol"; +import {ERC721EnumerableFacet} from "./ERC721EnumerableFacet.sol"; + +contract DeployDiamond { + function deploy() public { + // Facet implementations + ERC721EnumerableFacet erc721Facet = new ERC721EnumerableFacet(); + + // Diamond Cut data + DiamondCut.FacetCut[] memory cut = new DiamondCut.FacetCut[](1); + cut[0] = DiamondCut.FacetCut({ + facetAddress: address(erc721Facet), + action: DiamondCut.Action.ADD, + functionSelectors: Diamond.getSelectors(erc721Facet) + }); + + // Diamond Init data (if applicable, e.g., for name, symbol) + // For simplicity, init is omitted here but would typically be called + // via DiamondInit.DiamondInitInterface(diamondProxyAddress).initFacets(initData) + + // Deploy Diamond Proxy + DiamondProxy diamondProxy = new DiamondProxy(cut); + + // Example: Interact with the ERC721 facet after deployment + ERC721EnumerableFacet(diamondProxy).mint("0xOwnerAddress", 1); // Assuming a mint function exists and is added + uint256 supply = ERC721EnumerableFacet(diamondProxy).totalSupply(); + address owner = ERC721EnumerableFacet(diamondProxy).ownerOf(1); + } +} + +// Helper to get selectors (typically in a separate Diamond contract or utility) +library Diamond { + function getSelectors(address _facet) internal pure returns (bytes4[] memory selectors) { + // Implementation omitted for brevity; typically uses type casting and interface detection + return new bytes4[](0); // Placeholder + } +} +`} + + +## Best Practices + + +- Initialize the ERC721EnumerableFacet with essential metadata such as name and symbol during diamond deployment to ensure proper identification of the NFT collection. +- Carefully manage access control for functions like `approve` and `setApprovalForAll`, ensuring only the token owner or approved addresses can perform these actions. +- When upgrading the diamond, ensure the storage layout of the ERC721EnumerableFacet remains compatible. Avoid removing or reordering existing state variables. + + +## Security Considerations + + +This facet implements standard ERC721 logic; security relies on correct implementation of access controls and transfer logic. `safeTransferFrom` provides a layer of security against incompatible receiver contracts. Reentrancy is mitigated by standard ERC721 patterns, but thorough auditing of any custom `mint` or external interactions is recommended. Ensure that `internalTransferFrom` is only called by authorized internal logic. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx new file mode 100644 index 00000000..6591b8eb --- /dev/null +++ b/website/docs/contracts/facets/ERC721Facet.mdx @@ -0,0 +1,663 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "ERC-721 non-fungible token facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 non-fungible token facet for Compose diamonds + + + +- Full ERC-721 compliance, including core functions like `balanceOf`, `ownerOf`, `transferFrom`, and `approve`. +- Support for metadata retrieval via `tokenURI`. +- Includes `safeTransferFrom` variants for enhanced security during token transfers to potentially unknown recipient contracts. +- Internal `internalTransferFrom` function encapsulates core transfer logic, promoting code clarity and reuse within the facet. + + +## Overview + +The ERC721Facet provides a comprehensive implementation of the ERC-721 Non-Fungible Token standard within a Compose diamond. It manages token ownership, approvals, metadata, and transfers, enabling a diamond to act as a registry and issuer of unique digital assets. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose/contracts/src/facets/ERC721Facet.sol"; + +contract ERC721Consumer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function consumeERC721() external { + IERC721Facet erc721Facet = IERC721Facet(diamondAddress); + + string memory name = erc721Facet.name(); + string memory symbol = erc721Facet.symbol(); + uint256 balance = erc721Facet.balanceOf(address(this)); + // ... other ERC721 calls + } + + function transferMyToken(address to, uint256 tokenId) external { + IERC721Facet erc721Facet = IERC721Facet(diamondAddress); + erc721Facet.transferFrom(msg.sender, to, tokenId); + } +}`} + + +## Best Practices + + +- Initialize the ERC721Facet as part of the diamond deployment process, ensuring all necessary storage is correctly set up. +- When interacting with the ERC721Facet, always use the diamond's address as the target contract. +- Be mindful of gas costs associated with `safeTransferFrom` due to the on-chain checks for receiver compatibility. + + +## Security Considerations + + +Access control for functions like `approve` and `setApprovalForAll` is implicitly handled by the ERC-721 standard, requiring ownership or prior approval. The `safeTransferFrom` functions include checks to prevent accidental token loss when transferring to contracts that do not implement the ERC721Receiver interface. Reentrancy is mitigated by the standard ERC721 transfer patterns, where checks are performed before state changes and external calls. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx new file mode 100644 index 00000000..7da3d669 --- /dev/null +++ b/website/docs/contracts/facets/ExampleDiamond.mdx @@ -0,0 +1,129 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Diamond core facet for ERC-2535 implementation" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond core facet for ERC-2535 implementation + + + +- Implements ERC-2535 diamond proxy standard for modularity. +- Manages facet registration and function selector mapping for delegatecall routing. +- Supports diamond upgrades by adding, replacing, or removing facets. + + +## Overview + +The ExampleDiamond contract serves as the core implementation for an ERC-2535 compliant diamond proxy. It manages facet registration, function selector routing, and contract ownership, enabling modularity and upgradeability. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {ExampleDiamond} from "@compose/diamond-contracts/contracts/ExampleDiamond.sol"; + +// Example deployment script snippet +// Assume diamondCutFacet is deployed and its address is known +// Assume facetsToDeploy is an array of {facetAddress, action, functionSelectors} structs + +function deployDiamond(address[] memory facetAddresses, bytes[] memory facetBytecodes, address _owner) external { + // This is a simplified representation. Actual deployment involves creating facets + // and then cutting them into the diamond. + + // Example: Deploying and cutting facets + // address[] memory facetAddresses = new address[](1); + // bytes[] memory facetBytecodes = new bytes[](1); + // facetAddresses[0] = address(new MyFacet()); // Replace MyFacet with actual facet contract + // facetBytecodes[0] = type(MyFacet).creation.runtimeBytecode; + + // ExampleDiamond exampleDiamond = new ExampleDiamond(); + // exampleDiamond.diamondCut(facetsToDeploy, address(0), ""); // Simplified cut call + // exampleDiamond.transferOwnership(_owner); +}`} + + +## Best Practices + + +- Initialize the diamond with essential facets during deployment, including ownership and access control mechanisms. +- Ensure all facets added to the diamond are properly registered with their function selectors to enable correct routing. +- Carefully manage ownership transfers to maintain control over diamond upgrades and facet management. + + +## Security Considerations + + +The `constructor` function initializes the diamond and sets the owner. Access to `diamondCut` and `transferOwnership` functions is implicitly protected by ownership checks within the diamond's access control logic (not explicitly defined in this snippet but standard for diamond proxies). Ensure that the owner address is a secure multisig or EOA. Reentrancy is generally mitigated by the diamond proxy pattern as calls are typically delegatecalled into facets, but facets themselves must be audited for reentrancy. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx new file mode 100644 index 00000000..0e625356 --- /dev/null +++ b/website/docs/contracts/facets/OwnerFacet.mdx @@ -0,0 +1,216 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Ownership management facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Ownership management facet for Compose diamonds + + + +- Manages the single administrative owner of the diamond. +- Supports transferring ownership to a new address, including the ability to renounce ownership by setting the new owner to `address(0)`. +- Provides direct access to the owner's storage slot via `getStorage` for advanced inspection. + + +## Overview + +The OwnerFacet provides essential ownership management capabilities for a Compose diamond. It allows for retrieving the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet ensures a clear chain of control and administrative access within the diamond. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; + +contract OwnerConsumer { + IOwnerFacet internal ownerFacet; + + // Assume ownerFacet is initialized with the diamond's address + constructor(address _diamondAddress) { + ownerFacet = IOwnerFacet(_diamondAddress); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function transferControl(address _newOwner) external { + // Access control for transferOwnership would typically be implemented + // within the diamond's access control facet or by checking owner() here. + ownerFacet.transferOwnership(_newOwner); + } + + function abdicate() external { + // Ensure this contract is the current owner before renouncing + require(ownerFacet.owner() == address(this), "Not owner"); + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize the OwnerFacet with the diamond's address to interact with its ownership functions. +- Implement robust access control checks in calling facets or within the diamond's logic layer before invoking `transferOwnership` or `renounceOwnership`. +- When transferring ownership, ensure the `_newOwner` address is validated to prevent accidental lockouts. + + +## Security Considerations + + +The `transferOwnership` function is a critical administrative control. Ensure that calls to this function are protected by appropriate access control mechanisms (e.g., only callable by the current owner or a designated admin role). Renouncing ownership removes all administrative capabilities, making the contract effectively immutable from an administrative standpoint unless ownership is re-established through other means (which is not possible with this facet alone). + + +
+ +
+ + diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..8be6dc7c --- /dev/null +++ b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx @@ -0,0 +1,292 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Two-step ownership transfer facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Two-step ownership transfer facet for Compose diamonds + + + +- Two-step ownership transfer process for enhanced security. +- Explicit functions for viewing current and pending ownership states. +- Supports `renounceOwnership` to relinquish ownership. + + +## Overview + +The OwnerTwoStepsFacet manages contract ownership through a secure two-step transfer process. This facet provides functions to view the current and pending owner, initiate a transfer, and accept ownership, ensuring that ownership changes are deliberate and confirmed by both parties. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "@compose/contracts/src/facets/ownership/IOwnerTwoStepsFacet.sol"; +import {OwnerTwoStepsFacet} from "@compose/contracts/src/facets/ownership/OwnerTwoStepsFacet.sol"; + +contract OwnerSetup { + address public diamondAddress; + + // Assume diamondAddress is set during deployment + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function initiateOwnershipTransfer(address _newOwner) public { + IOwnerTwoStepsFacet(diamondAddress).transferOwnership(_newOwner); + } + + function acceptNewOwnership() public { + IOwnerTwoStepsFacet(diamondAddress).acceptOwnership(); + } + + function getCurrentOwner() public view returns (address) { + return IOwnerTwoStepsFacet(diamondAddress).owner(); + } + + function getPendingOwner() public view returns (address) { + return IOwnerTwoStepsFacet(diamondAddress).pendingOwner(); + } +}`} + + +## Best Practices + + +- Initialize ownership transfer using `transferOwnership` and confirm with `acceptOwnership` to prevent accidental ownership changes. +- Regularly check the `pendingOwner` to monitor ongoing ownership transfer requests. +- Integrate this facet into your diamond's access control strategy, ensuring critical functions are protected by ownership. + + +## Security Considerations + + +Ensure that only the current owner can call `transferOwnership` and `renounceOwnership`. The `acceptOwnership` function should only be callable by the address designated as the pending owner. Direct calls to `getOwnerStorage` and `getPendingOwnerStorage` bypass the public getter functions and should be used with caution, primarily for internal facet logic or advanced debugging. + + +
+ +
+ + diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx new file mode 100644 index 00000000..aeb1b516 --- /dev/null +++ b/website/docs/contracts/facets/RoyaltyFacet.mdx @@ -0,0 +1,195 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "ERC-2981 royalty facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-2981 royalty facet for Compose diamonds + + + +- Implements ERC-2981 `royaltyInfo` function. +- Supports token-specific royalty configurations. +- Falls back to a default royalty percentage when token-specific settings are not found. +- Utilizes inline assembly for efficient storage access. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, enabling diamonds to query royalty information for tokens. It provides a standardized interface for calculating royalty payments based on token-specific or default settings, facilitating revenue sharing for creators within the diamond ecosystem. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamond} from "@compose/diamond/contracts/IDiamond.sol"; +import {IRoyaltyFacet} from "@compose/diamond/contracts/facets/RoyaltyFacet.sol"; + +contract Deployer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getRoyaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address, uint256) { + // Get the RoyaltyFacet's address from the diamond + address royaltyFacetAddress = IDiamond(diamondAddress).getFacetAddress( + IRoyaltyFacet.ROYALTY_FACET_ID + ); + + // Call the royaltyInfo function on the RoyaltyFacet + return IRoyaltyFacet(royaltyFacetAddress).royaltyInfo(tokenId, salePrice); + } +}`} + + +## Best Practices + + +- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. +- Ensure the `STORAGE_POSITION` for royalty data is unique and managed by the diamond's storage contract. +- Access royalty storage via `getStorage` for read operations to maintain consistency. + + +## Security Considerations + + +The `royaltyInfo` function is view-only and does not modify state, mitigating reentrancy risks. Input validation for `tokenId` and `salePrice` is assumed to be handled by the caller or other facets. Ensure the `STORAGE_POSITION` is correctly defined and conflicts are avoided. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx new file mode 100644 index 00000000..1ac6c6a0 --- /dev/null +++ b/website/docs/contracts/modules/AccessControlMod.mdx @@ -0,0 +1,450 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Role-based access control (RBAC) module for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control (RBAC) module for Compose diamonds + + + +- Supports granting and revoking roles for accounts. +- Allows checking role membership with `hasRole` and enforcing it with `requireRole`. +- Enables setting administrative roles for other roles, creating a hierarchy. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlMod provides robust role-based access control (RBAC) for Compose diamonds. It allows for granular permission management, ensuring that only authorized accounts can execute sensitive functions, which is critical for maintaining the integrity and security of the diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlMod} from "@compose/modules/AccessControlMod.sol"; + +contract MyFacet { + IAccessControlMod internal accessControlMod; + + // Assume AccessControlMod is initialized and accessible via the diamond proxy + function initialize(IAccessControlMod _accessControlMod) external { + accessControlMod = _accessControlMod; + } + + function grantAdminRole(address _account) external { + // Granting the default ADMIN_ROLE to an account + accessControlMod.grantRole(IAccessControlMod.ADMIN_ROLE, _account); + } + + function onlyAdminCanDoThis() external { + // Requiring the caller to have the ADMIN_ROLE + accessControlMod.requireRole(IAccessControlMod.ADMIN_ROLE, msg.sender); + // ... perform admin-only action ... + } + + function canCallerDoThis() external view returns (bool) { + // Checking if the caller has a specific role + return accessControlMod.hasRole(IAccessControlMod.DEFAULT_ADMIN_ROLE, msg.sender); + } +}`} + + +## Best Practices + + +- Always use `requireRole` for critical functions to enforce access control, reverting with `AccessControlUnauthorizedAccount` on failure. +- Utilize `hasRole` for read-only checks or informational purposes, avoiding unnecessary reverts. +- Manage roles and their admins carefully, as incorrect configuration can lead to unintended access. + + +## Integration Notes + + +The AccessControlMod interacts with the diamond's storage to maintain role assignments and role admin configurations. Facets integrate by calling the module's functions through the diamond proxy. Changes to role assignments or role admin relationships are immediately reflected across all facets interacting with the module. It is crucial to ensure that the AccessControlMod is initialized correctly and that its storage is managed according to Compose's storage pattern guidelines to maintain compatibility and upgradeability. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx new file mode 100644 index 00000000..93ed3cbc --- /dev/null +++ b/website/docs/contracts/modules/AccessControlPausableMod.mdx @@ -0,0 +1,388 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Role-based access control with pause functionality for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Role-based access control with pause functionality for Compose diamonds + + + +- Role-based access control enforced at the function level. +- Granular pausing of individual roles, allowing selective disabling of functionality. +- Prevention of unauthorized access or operations when a role is paused. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlPausable module provides robust role-based access control combined with the ability to pause specific roles. This is crucial for diamonds requiring granular control over operations and the flexibility to temporarily halt certain functionalities without impacting the entire contract, enhancing safety and upgradeability. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausable} from "@compose/contracts/modules/access/AccessControlPausable.sol"; + +contract MyFacet { + IAccessControlPausable private accessControlPausable; + + // Assuming the diamond interface and storage are accessible + // interface IDiamond { + // function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external payable; + // function getFacetFunctionSelectors(address _facet) external view returns (bytes4[] memory); + // } + + // Example of how a facet might initialize or interact with the module + function initialize(address _accessControlPausableAddress) external { + accessControlPausable = IAccessControlPausable(_accessControlPausableAddress); + } + + // Example function protected by role and pause state + function sensitiveOperation() external { + // Replace 'ROLE_ADMIN' with the actual role identifier + accessControlPausable.requireRoleNotPaused(msg.sender, "ROLE_ADMIN"); + // ... perform sensitive operation ... + } + + // Example of pausing a role (likely by an authorized entity) + function pauseAdminRole() external { + // Assuming the caller has the necessary permissions to pause + accessControlPausable.pauseRole(msg.sender, "ROLE_ADMIN"); + } + + // Example of unpausing a role + function unpauseAdminRole() external { + // Assuming the caller has the necessary permissions to unpause + accessControlPausable.unpauseRole(msg.sender, "ROLE_ADMIN"); + } +}`} + + +## Best Practices + + +- Always use `requireRoleNotPaused` to enforce both role membership and pause status before executing sensitive operations. +- Ensure that only authorized entities can call `pauseRole` and `unpauseRole` by implementing appropriate access control within your facet or relying on the diamond's governance. +- Handle the specific `AccessControlUnauthorizedAccount` and `AccessControlRolePaused` errors appropriately in your frontend or consuming contracts to provide clear user feedback. + + +## Integration Notes + + +This module interacts with the diamond's storage to manage role pause states. Facets can access this functionality through the `IAccessControlPausable` interface. The `requireRoleNotPaused` function implicitly checks for role existence and pause status, reverting with specific errors if checks fail. It's crucial to ensure the AccessControlPausable module is correctly initialized and its storage slot is managed within the diamond's overall storage layout. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..12264d4f --- /dev/null +++ b/website/docs/contracts/modules/AccessControlTemporalMod.mdx @@ -0,0 +1,479 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Time-limited role-based access control for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Time-limited role-based access control for Compose diamonds + + + +- Time-limited role assignments: Roles automatically expire after a specified timestamp. +- Automatic expiry checks: `requireValidRole` verifies both role existence and non-expiry. +- Explicit revocation: `revokeTemporalRole` allows for immediate removal of temporal roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControlTemporalMod provides time-limited role-based access control, ensuring that granted permissions expire automatically. This enhances diamond security by enforcing temporary access, reducing the risk of stale permissions persisting indefinitely. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; +import {AccessControlUnauthorizedAccount, AccessControlRoleExpired} from "@compose/modules/access-control-temporal/Errors.sol"; + +contract MyFacet { + IAccessControlTemporalMod public constant ACCESS_CONTROL_TEMPORAL_MOD = IAccessControlTemporalMod(address(0)); // Replace with actual deployed address + + bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + + function grantOperatorRole(address _account, uint64 _expiry) external { + ACCESS_CONTROL_TEMPORAL_MOD.grantRoleWithExpiry(OPERATOR_ROLE, _account, _expiry); + } + + function executeOperation() external { + // Check if the caller has a valid, non-expired OPERATOR_ROLE + ACCESS_CONTROL_TEMPORAL_MOD.requireValidRole(OPERATOR_ROLE, msg.sender); + + // Operation logic here + } + + function revokeOperatorRole(address _account) external { + ACCESS_CONTROL_TEMPORAL_MOD.revokeTemporalRole(OPERATOR_ROLE, _account); + } +}`} + + +## Best Practices + + +- Use `requireValidRole` to enforce time-bound access to critical functions, preventing unauthorized actions after a role's expiry. +- Carefully manage role expiry timestamps to ensure timely revocation and maintain the principle of least privilege. +- Handle `AccessControlUnauthorizedAccount` and `AccessControlRoleExpired` errors to provide clear feedback to users when access is denied due to missing or expired roles. + + +## Integration Notes + + +This module interacts with the diamond's storage. Facets should import and use the `IAccessControlTemporalMod` interface. The `grantRoleWithExpiry`, `revokeTemporalRole`, and `requireValidRole` functions operate on the temporal access control state managed by this module. Ensure that the deployment process correctly initializes and links this module to the diamond proxy. The module relies on underlying access control mechanisms for role management and adds temporal constraints. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx new file mode 100644 index 00000000..39b5c281 --- /dev/null +++ b/website/docs/contracts/modules/DiamondCutMod.mdx @@ -0,0 +1,414 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Diamond upgrade (cut) module for ERC-2535 diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond upgrade (cut) module for ERC-2535 diamonds + + + +- Enables dynamic addition, replacement, and removal of facets, providing flexibility for diamond upgrades. +- Supports executing an arbitrary function call via `delegatecall` immediately after a cut operation, allowing for initialization or post-upgrade logic execution. +- Provides granular control over individual function selectors within facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCutMod provides essential functionalities for managing facets within an ERC-2535 compliant diamond proxy. It enables precise control over diamond upgrades, allowing developers to add, replace, or remove functions, ensuring a robust and flexible upgrade path for diamond applications. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-contracts/contracts/diamond/interfaces/IDiamondCut.sol"; +import {DiamondCutMod} from "@compose/diamond-contracts/contracts/modules/DiamondCutMod.sol"; + +contract MyFacetManager { + IDiamondCut public diamondCutFacet; + + constructor(address _diamondCutFacetAddress) { + diamondCutFacet = IDiamondCut(_diamondCutFacetAddress); + } + + /** + * @notice Adds a new function to the diamond. + * @param _facetAddress The address of the facet contract. + * @param _functionSelectors An array of function selectors to add. + */ + function addFacet(address _facetAddress, bytes4[] memory _functionSelectors) external { + diamondCutFacet.diamondCut(_getAddFacet(_facetAddress, _functionSelectors), address(0), \"\", \"\"); + } + + /** + * @notice Replaces an existing function in the diamond. + * @param _facetAddress The address of the new facet contract. + * @param _functionSelectors An array of function selectors to replace. + */ + function replaceFacet(address _facetAddress, bytes4[] memory _functionSelectors) external { + diamondCutFacet.diamondCut(_getReplaceFacet(_facetAddress, _functionSelectors), address(0), \"\", \"\"); + } + + /** + * @notice Removes functions from the diamond. + * @param _functionSelectors An array of function selectors to remove. + */ + function removeFacetFunctions(bytes4[] memory _functionSelectors) external { + diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), address(0), \"\", _functionSelectors); + } + + // Helper functions to format FacetCut structs for diamondCut + function _getAddFacet(address _facetAddress, bytes4[] memory _functionSelectors) internal pure returns (IDiamondCut.FacetCut[] memory) { + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: _facetAddress, + action: IDiamondCut.Action.ADD, + selectors: _functionSelectors + }); + return cuts; + } + + function _getReplaceFacet(address _facetAddress, bytes4[] memory _functionSelectors) internal pure returns (IDiamondCut.FacetCut[] memory) { + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: _facetAddress, + action: IDiamondCut.Action.REPLACE, + selectors: _functionSelectors + }); + return cuts; + } +} `} + + +## Best Practices + + +- Ensure that only authorized entities can call `diamondCut`, `addFunctions`, `removeFunctions`, and `replaceFunctions` to maintain control over diamond upgrades. +- Carefully review function selectors before adding, replacing, or removing them to prevent unintended loss of functionality or introduction of vulnerabilities. +- When replacing facets, ensure that the new facet's functions are compatible with existing state and logic to avoid breaking the diamond's overall functionality. + + +## Integration Notes + + +The DiamondCutMod interacts directly with the diamond proxy's storage to manage facet mappings. The `diamondCut` function modifies the internal registry of facets and their associated function selectors. Any changes made through `diamondCut`, `addFunctions`, `removeFunctions`, or `replaceFunctions` are immediately reflected in the diamond's behavior. The order of operations within `diamondCut` is critical; additions, replacements, and removals are processed sequentially. The `getStorage` function can be used to inspect the current state of facet mappings. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx new file mode 100644 index 00000000..1a16e93c --- /dev/null +++ b/website/docs/contracts/modules/DiamondMod.mdx @@ -0,0 +1,234 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Diamond Library - Internal functions and storage for diamond proxy functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Diamond Library - Internal functions and storage for diamond proxy functionality. + + + +- `addFacets`: Enables programmatic addition of facets and their function selectors, restricted to deployment time. +- `diamondFallback`: Acts as the core dispatch mechanism, routing external calls to the correct facet. +- `getStorage`: Provides access to the diamond's internal storage, allowing facets to introspect or interact with shared state. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondMod library provides essential internal functions and storage management for the Compose diamond proxy. It facilitates the addition of facets during deployment and enables the diamond to route calls to the appropriate facet, ensuring modularity and composability. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondMod} from "@compose/diamond/contracts/DiamondMod.sol"; + +contract MyFacet { + DiamondMod internal diamondMod; + + constructor(address _diamondModAddress) { + diamondMod = DiamondMod(_diamondModAddress); + } + + /** + * @notice Example of calling getStorage from a facet. + */ + function readDiamondStorage() external view returns (bytes memory) { + return diamondMod.getStorage(); + } +}`} + + +## Best Practices + + +- Facet functions should be `external` or `internal` only, as per Compose guidelines. +- Use `addFacets` exclusively during diamond deployment to ensure stable function mappings. +- Handle potential `diamondFallback` reentrancy or execution errors appropriately within calling facets. + + +## Integration Notes + + +DiamondMod manages the core diamond proxy's function selector to facet address mappings. Facets interact with DiamondMod via its external functions. Changes made by `addFacets` are permanent for the lifetime of the diamond proxy. The `getStorage` function returns the raw storage of the diamond, which facets can interpret based on their understanding of the diamond's storage layout. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx new file mode 100644 index 00000000..f52791bd --- /dev/null +++ b/website/docs/contracts/modules/ERC1155Mod.mdx @@ -0,0 +1,618 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens. + + + +- Supports both single and batch transfers, minting, and burning operations for maximum efficiency. +- Includes robust safe transfer logic with receiver validation, adhering to EIP-1155 standards for secure token handling. +- Provides functionality to manage token URIs, allowing for dynamic metadata configuration. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod provides a robust implementation of the ERC-1155 Multi-Token Standard, enabling diamonds to manage fungible and non-fungible tokens efficiently. It ensures safe token transfers, batch operations, and URI management, crucial for complex token economies and composability. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import { ERC1155Mod } from "./ERC1155Mod.sol"; + +contract MyERC1155Facet is ERC1155Mod, IERC1155 { + // Storage for ERC1155Mod is managed by the diamond storage pattern. + + // Example: Minting tokens + function mintNewTokens(address to, uint256 id, uint256 amount) external { + // Assuming this facet has the necessary permissions to mint + mint(to, id, amount); + } + + // Example: Safe transfer + function transferMyTokens(address from, address to, uint256 id, uint256 amount, bytes calldata data) external { + // Assuming this facet has the necessary permissions and approvals + safeTransferFrom(from, to, id, amount, data); + } + + // Implement required IERC1155Receiver functions if this facet acts as a receiver + function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) { + // Handle received ERC1155 tokens + return + bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")); + } + + function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) { + // Handle received batch of ERC1155 tokens + return + bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")); + } + + // ... other ERC1155 functions like balanceOf, uri, etc. would be implemented here or in other facets ... +}`} + + +## Best Practices + + +- Ensure the facet has appropriate access control for minting, burning, and URI setting functions. Rely on the diamond's access control mechanism. +- When implementing `onERC1155Received` and `onERC1155BatchReceived`, validate `data` or other parameters as necessary to ensure safe handling of incoming tokens. +- Be mindful of gas costs for batch operations; encourage users to batch when feasible, but understand single operations might be necessary. + + +## Integration Notes + + +The ERC1155Mod interacts with a predefined storage slot within the diamond's storage layout. Facets using this module can access and modify the ERC-1155 state (balances, URIs, etc.) through the module's functions. The `getStorage` function provides direct access to the ERC1155 storage struct, allowing for advanced logic or direct manipulation if needed, though direct manipulation should be done with extreme care to maintain invariants. Facets should ensure they do not introduce conflicting state or logic that violates ERC-1155 invariants. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx new file mode 100644 index 00000000..280e7436 --- /dev/null +++ b/website/docs/contracts/modules/ERC165Mod.mdx @@ -0,0 +1,155 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. + + + +- Standardized ERC-165 interface detection across diamond facets. +- Centralized interface registration and querying logic. +- Enables external contract compatibility and discovery of diamond capabilities. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod module provides a standardized way for facets within a Compose diamond to implement and expose ERC-165 interface detection. This is crucial for composability, allowing external contracts to reliably query which interfaces a diamond supports without needing to know its specific facet implementation details. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC165, IERC165} from "@compose/modules/erc165/LibERC165.sol"; + +contract MyERC721Facet { + struct Storage { + LibERC165.ERC165Storage erc165; + // ... other ERC721 storage variables + } + + function initialize(Storage storage self) external { + LibERC165.registerInterface(self.erc165, type(IERC721).interfaceId); + } + + function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { + return LibERC165.supportsInterface(LibERC165.getStorage(address(this)), interfaceId); + } +}`} + + +## Best Practices + + +- Ensure `LibERC165.registerInterface` is called during facet initialization for all supported interfaces. +- Always use `LibERC165.supportsInterface` to check for interface support, leveraging the diamond's ERC165 state. +- Avoid directly manipulating the `erc165` storage variable; use the provided library functions. + + +## Integration Notes + + +The ERC165Mod relies on a dedicated `ERC165Storage` struct, which must be included in the main diamond storage layout or within individual facet storage structs. The `getStorage` function uses inline assembly to precisely locate this storage at a fixed slot, ensuring consistent access across all facets. Facets should call `LibERC165.registerInterface` during their initialization to populate the `ERC165Storage` with their supported interface IDs. Changes to the registered interfaces are immediately visible through `LibERC165.supportsInterface`. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..7f78a3ba --- /dev/null +++ b/website/docs/contracts/modules/ERC20BridgeableMod.mdx @@ -0,0 +1,424 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "LibERC20Bridgeable — ERC-7802 Library" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Bridgeable — ERC-7802 Library + + + +- Enforces `trusted-bridge` role for cross-chain minting and burning operations. +- Provides helper functions to access underlying ERC20 and AccessControl storage slots. +- Designed for integration into Compose diamond proxies, leveraging the storage pattern. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20BridgeableMod provides cross-chain interoperability for ERC20 tokens within a Compose diamond. It enforces access control for bridging operations, ensuring only trusted entities can mint or burn tokens across different chains, enhancing the security and composability of your token deployments. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableMod} from "@compose/contracts/modules/erc20/ERC20BridgeableMod.sol"; + +contract MyFacet { + address internal diamondAddress; + + // Assume diamondAddress is set during deployment or initialization + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnForBridge(address _token, uint256 _amount) external { + // Call the crosschainBurn function through the diamond proxy + // The diamond proxy will route this to the ERC20BridgeableMod facet + IERC20BridgeableMod(diamondAddress).crosschainBurn(_token, _amount); + } + + function mintForBridge(address _token, address _to, uint256 _amount) external { + // Call the crosschainMint function through the diamond proxy + IERC20BridgeableMod(diamondAddress).crosschainMint(_token, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is correctly managed within the AccessControl facet to restrict who can call `crosschainBurn` and `crosschainMint`. +- Always verify the outcome of cross-chain operations on the destination chain, as this module only handles the minting/burning initiation. +- Use `checkTokenBridge` internally before initiating cross-chain transfers from trusted bridge addresses to prevent unauthorized calls. + + +## Integration Notes + + +The ERC20BridgeableMod interacts with diamond storage slots defined by Compose. Specifically, it reads from the AccessControl storage to verify the `trusted-bridge` role and the ERC20 storage for token-specific details. Facets interacting with this module should call its functions through the diamond proxy. The `getAccessControlStorage` and `getERC20Storage` functions return references to these storage areas, allowing other facets to inspect the state without direct storage slot manipulation. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx new file mode 100644 index 00000000..89dfc266 --- /dev/null +++ b/website/docs/contracts/modules/ERC20Mod.mdx @@ -0,0 +1,424 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic. + + + +- Provides essential ERC-20 functions: `mint`, `burn`, `transfer`, `transferFrom`, `approve`. +- Manages ERC-20 specific storage via `IERC20ModStorage`. +- Utilizes inline assembly in `getStorage` for direct access to the storage struct at a fixed slot. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod module provides a standardized, internal library for implementing ERC-20 token functionality within a Compose diamond. It manages ERC-20 specific storage and offers essential functions like minting, burning, transferring, and approving allowances, ensuring consistent and safe token operations across facets. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod, IERC20ModStorage} from "@compose/modules/ERC20Mod.sol"; + +contract MyERC20Facet { + IERC20Mod public erc20mod; + + function initialize(address _erc20ModAddress) external { + erc20mod = IERC20Mod(_erc20ModAddress); + } + + function mintTokens(address _to, uint256 _amount) external { + // Assuming access control is handled externally or by another facet + erc20mod.mint(_to, _amount); + } + + function transferTokens(address _from, address _to, uint256 _amount) external { + // Assuming access control and allowance checks are handled + erc20mod.transfer(_from, _to, _amount); + } + + function approveSpender(address _spender, uint256 _amount) external { + erc20mod.approve(msg.sender, _spender, _amount); + } +}`} + + +## Best Practices + + +- Ensure proper access control mechanisms are in place before calling mint or burn functions, as these modify critical token state. +- Always verify that the necessary allowances are set before calling `transferFrom` by using `getStorage` to inspect the allowance struct. +- Handle potential `ERC20TransferErrors` and `ERC20AllowanceErrors` gracefully in consuming facets. + + +## Integration Notes + + +The ERC20Mod relies on a dedicated storage slot for its `IERC20ModStorage` struct, which contains balances, allowances, and total supply. Facets interacting with ERC20Mod should use the `getStorage` function to obtain a pointer to this struct. This ensures that all ERC20 operations consistently read from and write to the correct storage location, maintaining the integrity of the ERC-20 state within the diamond. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx new file mode 100644 index 00000000..e31b051a --- /dev/null +++ b/website/docs/contracts/modules/ERC20PermitMod.mdx @@ -0,0 +1,282 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage" +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage + + + +- Implements EIP-712 compliant domain separator generation for secure signature verification. +- Provides a robust `permit` function to validate and process ERC-2612 signature approvals. +- Designed for seamless integration with Compose diamond storage patterns. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20PermitMod library provides essential logic for implementing ERC-2612 permit functionality within a Compose diamond. It manages domain separator calculation and permit validation, enabling gasless token approvals via EIP-712 signatures. This modular approach ensures consistent and secure permit handling across diamond facets. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC20Permit} from "@compose/contracts/src/modules/erc20/LibERC20Permit.sol"; +import {ERC20PermitStorage} from "@compose/contracts/src/modules/erc20/ERC20PermitStorage.sol"; +import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; + +contract MyERC20PermitFacet { + using LibERC20Permit for ERC20PermitStorage; + + ERC20PermitStorage private _permitStorage; + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Ensure the permit storage is initialized via the diamond initializer + // For example: _permitStorage = ERC20PermitStorage(LibDiamond.diamondStorage().erc20PermitStorage); + + // Call the library function to validate and set allowance + // The emit Approval event must be handled by the calling facet. + _permitStorage.permit(owner, spender, value, deadline, v, r, s); + } + + // Example of accessing domain separator (usually done by external signers) + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _permitStorage.DOMAIN_SEPARATOR(); + } +}`} + + +## Best Practices + + +- Ensure `ERC20PermitStorage` is properly initialized by the diamond's initializer function before any calls to `permit` or `DOMAIN_SEPARATOR` are made. +- The calling facet is responsible for emitting the `Approval` event as specified by ERC-20 and ERC-2612 standards after a successful `permit` call. +- Always verify the `deadline` provided in the permit signature to prevent stale approvals. + + +## Integration Notes + + +The ERC20PermitMod library relies on the `ERC20PermitStorage` struct. This struct must be included in the diamond's overall storage layout and initialized correctly. The `LibERC20Permit` functions operate directly on this storage. The `DOMAIN_SEPARATOR` function computes its value based on the contract address and chain ID, and this value is stored within the `ERC20PermitStorage`. Facets using this module will interact with `ERC20PermitStorage` via the library functions. The `permit` function itself does not emit the `Approval` event; this is a responsibility delegated to the calling facet to maintain explicit control over event emissions. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx new file mode 100644 index 00000000..16debd53 --- /dev/null +++ b/website/docs/contracts/modules/ERC6909Mod.mdx @@ -0,0 +1,528 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic. + + + +- Provides foundational logic for ERC-6909 token management, including minting, burning, transfers, and approvals. +- Leverages the diamond storage pattern for efficient and upgradeable state management. +- Supports operator functionality for delegated token management. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC6909Mod module provides the core logic and storage for implementing the ERC-6909 minimal multi-token standard within a Compose diamond. It enables efficient management of multiple token types, including minting, burning, transferring, and approvals, all while adhering to the diamond's storage pattern for composability and upgradeability. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod} from "./IERC6909Mod.sol"; +import {LibDiamond} from "./diamond/LibDiamond.sol"; + +contract MyERC6909Facet { + uint256 constant ERC6909_STORAGE_SLOT = 1; // Example slot + + struct ERC6909Storage { + mapping(uint256 => mapping(address => uint256)) allowances; + mapping(uint256 => mapping(address => uint256)) balances; + mapping(address => mapping(address => bool)) operators; + } + + function _getERC6909Storage() internal pure returns (ERC6909Storage storage) { + return LibDiamond.getStorage(ERC6909_STORAGE_SLOT); + } + + function mintToken(uint256 _id, uint256 _amount, address _to) external { + ERC6909Storage storage erc6909 = _getERC6909Storage(); + // Call the internal mint function from the ERC6909Mod library + IERC6909Mod.mint(erc6909, _id, _amount, _to); + } + + function transferToken(uint256 _id, uint256 _amount, address _from, address _to) external { + ERC6909Storage storage erc6909 = _getERC6909Storage(); + // Call the internal transfer function from the ERC6909Mod library + IERC6909Mod.transfer(erc6909, _id, _amount, _from, _to); + } +}`} + + +## Best Practices + + +- Ensure the `ERC6909Mod` library is correctly integrated into the diamond's facet registry and storage layout. +- Implement proper access control for functions that modify state (e.g., `mint`, `burn`, `setOperator`) within your facets. +- Be mindful of `type(uint256).max` allowances and operator logic during transfers to prevent unintended state changes. + + +## Integration Notes + + +The ERC6909Mod library interacts with a dedicated storage slot within the diamond's global storage. Facets that implement ERC-6909 functionality must correctly reference this storage slot, typically via a `LibDiamond.getStorage()` call, to access and manipulate the `ERC6909Storage` struct. The ordering of storage variables within the `ERC6909Storage` struct is critical and must not be altered to maintain backward compatibility and correct state access across diamond upgrades. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..c3ee77bc --- /dev/null +++ b/website/docs/contracts/modules/ERC721EnumerableMod.mdx @@ -0,0 +1,347 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. + + + +- Manages the addition and removal of tokens from internal enumeration lists during minting, burning, and transfers. +- Utilizes inline assembly via `getStorage` to efficiently access and manipulate the ERC-721 enumerable storage struct from its designated diamond storage slot. +- Enforces basic validation such as non-zero receiver addresses for minting and token existence checks for burn/transfer operations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721EnumerableMod module provides essential internal logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures that minted, burned, and transferred tokens are correctly tracked in enumeration lists, maintaining the integrity of token ownership and order. This is crucial for features like listing all tokens or tracking token indices. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "@compose/modules/ERC721/ERC721EnumerableMod.sol"; + +contract MyERC721Facet { + // Assume ERC721EnumerableMod is deployed and its address is known + IERC721EnumerableMod private constant _erc721Enumerable = IERC721EnumerableMod(0x...); + + function mintToken(address _to, uint256 _tokenId) external { + // Before minting, ensure token does not exist (using ERC721 core logic, not shown here) + // ... + _erc721Enumerable.mint(_to, _tokenId); + // ... + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + // Perform ownership and approval checks (using ERC721 core logic, not shown here) + // ... + _erc721Enumerable.transferFrom(_from, _to, _tokenId); + // ... + } + + function burnToken(uint256 _tokenId) external { + // Perform ownership checks (using ERC721 core logic, not shown here) + // ... + _erc721Enumerable.burn(_tokenId); + // ... + } +}`} + + +## Best Practices + + +- Always perform necessary ERC-721 ownership and approval checks within your facet before calling `transferFrom` or `burn` from this module. +- Ensure the `mint` function is called with a valid, non-zero receiver address and that the token ID does not already exist to prevent state corruption. +- Handle potential reverts from this module gracefully, providing clear error messages to users. + + +## Integration Notes + + +The ERC721EnumerableMod interacts directly with the diamond's storage. The `getStorage` function, using inline assembly, retrieves a pointer to the `ERC721EnumerableStorage` struct located at a predefined storage slot within the diamond. Facets using this module must be aware that `mint`, `burn`, and `transferFrom` directly modify this shared storage, impacting the enumeration order and count of all ERC-721 tokens managed by the diamond. The ordering of storage variables within the `ERC721EnumerableStorage` struct is critical and must be preserved if the struct is extended. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx new file mode 100644 index 00000000..60e25bf5 --- /dev/null +++ b/website/docs/contracts/modules/ERC721Mod.mdx @@ -0,0 +1,354 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. + + + +- Provides atomic operations for minting, transferring, and burning ERC-721 tokens. +- Abstracts direct diamond storage access for ERC-721 state, simplifying facet development. +- Enforces ERC-721 standard invariants through its internal logic. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod module provides essential internal logic for managing ERC-721 tokens within a Compose diamond. It enables custom facets to implement ERC-721 functionality by abstracting direct interaction with diamond storage, promoting code reuse and adherence to the standard. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod} from "@compose/modules/ERC721Mod.sol"; + +contract MyERC721Facet { + IERC721Mod public constant ERC721Mod = IERC721Mod(address(0)); // Replace with actual diamond address + + function mintToken(address _to, uint256 _tokenId) external { + ERC721Mod.mint(_to, _tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + ERC721Mod.transferFrom(_from, _to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + ERC721Mod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Mod` contract address is correctly set or imported within facets that utilize its functions. +- Validate all input parameters, especially addresses and token IDs, before calling module functions to prevent unexpected reverts. +- Understand that this module directly manipulates diamond storage; any changes to its internal storage layout require careful consideration for upgrade compatibility. + + +## Integration Notes + + +The ERC721Mod interacts with diamond storage at a predefined slot to manage ERC-721 token ownership, approvals, and metadata. Facets using this module should be aware that any changes to the internal storage structure of ERC721Mod (e.g., adding new fields to its storage struct) may require diamond upgrades and careful migration strategies to maintain state continuity. The `getStorage` function allows direct access to this internal storage if needed by advanced facets. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx new file mode 100644 index 00000000..9ddf87d2 --- /dev/null +++ b/website/docs/contracts/modules/NonReentrancyMod.mdx @@ -0,0 +1,142 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. + + + +- Provides `enter()` and `exit()` functions to manage reentrancy locks. +- Designed for integration into custom facets or as a library function using `using for`. +- Aids in maintaining state consistency by preventing recursive function calls within a single transaction context. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides essential utilities to prevent reentrant calls within your diamond facets. By implementing reentrancy guards, it ensures the integrity and security of your contract's state during complex interactions, which is crucial for maintaining predictable execution flows in a composable diamond architecture. + +--- + +## Storage + +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/core/src/modules/NonReentrancyMod.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 internal _reentrancyGuard; + + /** + * @notice Performs an action that must be non-reentrant. + */ + function sensitiveAction() external { + // Enter the non-reentrancy guard. + _reentrancyGuard.enter(); + + try { + // Perform sensitive operations here. + // ... + } finally { + // Exit the non-reentrancy guard. + _reentrancyGuard.exit(); + } + } +}`} + + +## Best Practices + + +- Always ensure `exit()` is called, even if an error occurs during the protected operation. Use `try...finally` blocks for robust error handling. +- Store the reentrancy guard variable within your facet's storage, following Compose's storage pattern guidelines. +- Consider the implications of reentrancy when designing interactions between different facets; this module provides a fundamental safeguard. + + +## Integration Notes + + +The `NonReentrancyMod` is typically integrated by adding a `uint256` variable to the facet's storage to act as the reentrancy guard. This variable is then managed by the `enter()` and `exit()` functions. Facets that wish to use this module should include it in their storage layout and apply the library functions to their designated guard variable. The guard variable's state (locked or unlocked) is local to the facet using it. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx new file mode 100644 index 00000000..a0cc51f4 --- /dev/null +++ b/website/docs/contracts/modules/OwnerMod.mdx @@ -0,0 +1,253 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. + + + +- Manages contract ownership according to ERC-173 standards. +- Provides atomic `requireOwner()` check for immediate access control. +- Supports explicit `transferOwnership()` for secure transitions. +- Allows retrieval of owner address via `owner()` and direct storage access via `getStorage()`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerMod module provides essential ERC-173 compliant contract ownership management for Compose diamonds. It defines storage for the contract owner and offers functions to retrieve, check, and update ownership, ensuring that critical operations can be restricted to authorized addresses within the diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "../modules/OwnerMod.sol"; + +contract MyFacet { + uint256 constant OWNER_MOD_STORAGE_SLOT = 0x1234567890abcdef; // Example slot + + function initialize(address initialOwner) external { + IOwnerMod(OWNER_MOD_STORAGE_SLOT).setContractOwner(initialOwner); + } + + function changeOwner(address newOwner) external { + IOwnerMod(OWNER_MOD_STORAGE_SLOT).transferOwnership(newOwner); + } + + function getContractOwner() external view returns (address) { + return IOwnerMod(OWNER_MOD_STORAGE_SLOT).owner(); + } + + function onlyOwnerAction() external { + IOwnerMod(OWNER_MOD_STORAGE_SLOT).requireOwner(); + // Perform owner-specific action + } +}`} + + +## Best Practices + + +- Use `requireOwner()` judiciously for critical administrative functions to enforce access control based on contract ownership. +- Ensure the `OwnerMod` is initialized with the correct initial owner during diamond deployment or upgrade. +- Handle ownership transfers carefully; renouncing ownership by setting the new owner to `address(0)` should be a deliberate action. + + +## Integration Notes + + +The `OwnerMod` utilizes a dedicated storage slot (defined by `STORAGE_POSITION` within the module's implementation) to store its ownership-related state. Facets interact with this module by casting the storage slot address to the `IOwnerMod` interface. Any facet can call `setContractOwner`, `transferOwnership`, `owner`, or `requireOwner` as long as the `OwnerMod` facet is deployed to the diamond and the caller has the correct permissions or context to interact with the diamond's storage. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..cd623fd7 --- /dev/null +++ b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx @@ -0,0 +1,318 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. + + + +- Implements a secure two-step ownership transfer process, mitigating risks of accidental ownership loss. +- Provides `owner()` and `pendingOwner()` view functions for transparent state inspection. +- Includes `requireOwner()` for enforcing access control on sensitive operations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerTwoStepsMod provides a secure, two-step ownership transfer mechanism, crucial for managing diamond proxies and their facets. This pattern prevents accidental loss of control by requiring explicit acceptance of ownership, enhancing safety and composability within the diamond architecture. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {OwnerTwoStepsMod} from "../modules/OwnerTwoStepsMod.sol"; + +contract MyFacet is OwnerTwoStepsMod { + // Assuming OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are defined + // and initialized correctly within the diamond deployment. + + /** + * @notice Transfers ownership to a new address. + * @param _newOwner The address to transfer ownership to. + */ + function initiateOwnershipTransfer(address _newOwner) external { + transferOwnership(_newOwner); + } + + /** + * @notice Accepts an incoming ownership transfer. + */ + function acceptNewOwnership() external { + acceptOwnership(); + } + + /** + * @notice Renounces current ownership. + */ + function giveUpOwnership() external { + renounceOwnership(); + } + + /** + * @notice Returns the current owner of the diamond. + */ + function getCurrentOwner() external view returns (address) { + return owner(); + } + + /** + * @notice Returns the pending owner of the diamond. + */ + function getPendingOwnerAddress() external view returns (address) { + return pendingOwner(); + } +} +`} + + +## Best Practices + + +- Always call `transferOwnership` from the current owner and `acceptOwnership` from the pending owner to ensure a valid two-step transfer. +- Use `requireOwner` to protect critical administrative functions within your facets. +- Be aware that `renounceOwnership` permanently removes owner privileges; use with extreme caution. + + +## Integration Notes + + +This module interacts with diamond storage by reading and writing to specific storage slots designated for the owner and pending owner. Facets integrating this module must ensure that `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are correctly defined and initialized within the diamond's storage layout. Changes to these storage variables are immediately visible to all facets interacting with the diamond. + + +
+ +
+ + diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx new file mode 100644 index 00000000..c91d7e39 --- /dev/null +++ b/website/docs/contracts/modules/RoyaltyMod.mdx @@ -0,0 +1,364 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic." +gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic. + + + +- Implements ERC-2981 `royaltyInfo` standard for on-chain royalty queries. +- Supports both default royalties applicable to all tokens and token-specific overrides. +- Utilizes a dedicated storage slot for efficient access to royalty data via inline assembly. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The RoyaltyMod module provides essential ERC-2981 compliant royalty logic for Compose diamonds. It enables setting and querying default and token-specific royalties, ensuring creators are compensated on secondary sales. This module is crucial for marketplaces and NFT platforms built on Compose. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "../interfaces/IRoyaltyMod.sol"; + +contract RoyaltyFacet { + // Assume IRoyaltyMod is correctly interfaced and the diamond proxy is implemented + IRoyaltyMod internal royaltyMod; + + constructor(address _diamondProxyAddress) { + royaltyMod = IRoyaltyMod(_diamondProxyAddress); + } + + /** + * @notice Sets a default royalty for all tokens. + * @param _receiver The address to receive royalty payments. + * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). + */ + function grantDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setDefaultRoyalty(_receiver, _feeBasisPoints); + } + + /** + * @notice Sets a specific royalty for a token ID. + * @param _tokenId The ID of the token to set royalty for. + * @param _receiver The address to receive royalty payments. + * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). + */ + function grantTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Queries royalty information for a given token and sale price. + * @param _tokenId The ID of the token. + * @param _salePrice The total sale price of the token. + * @return receiver The address to receive royalty payments. + * @return feeAmount The calculated royalty amount. + */ + function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 feeAmount) { + (receiver, feeAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); + return (receiver, feeAmount); + } +} +`} + + +## Best Practices + + +- Ensure the `_receiver` address is validated to prevent sending royalties to unintended accounts. +- Use `deleteDefaultRoyalty` or `resetTokenRoyalty` judiciously, understanding that `royaltyInfo` will then return `(address(0), 0)` for tokens without specific royalty settings. +- Be aware that setting token-specific royalties overrides default settings; ensure this behavior aligns with your application's requirements. + + +## Integration Notes + + +The RoyaltyMod library interacts with diamond storage by reading and writing to a predefined storage slot managed by the diamond proxy. Facets using this module will call its functions, which in turn access and modify the shared royalty storage. The `getStorage` function provides direct access to the `RoyaltyStorage` struct, which contains `defaultRoyalty` and `tokenRoyalties` mappings. Changes made through `setDefaultRoyalty` and `setTokenRoyalty` are immediately visible to subsequent calls to `royaltyInfo`. + + +
+ +
+ + From 00106e341b257629f718c654a349c0ad60a7cf8a Mon Sep 17 00:00:00 2001 From: MN Date: Fri, 19 Dec 2025 22:01:00 -0500 Subject: [PATCH 39/68] try mirroring /src to docs --- .github/docs-gen-prompts.md | 32 +- .github/scripts/ai-provider/rate-limiter.js | 6 - .../generate-docs-utils/ai-enhancement.js | 34 +- .../generate-docs-utils/category-generator.js | 484 +++++++++++ .github/scripts/generate-docs-utils/config.js | 111 ++- .../doc-generation-utils.js | 360 +++++---- .github/scripts/generate-docs.js | 152 ++-- .github/scripts/sync-docs-structure.js | 210 +++++ .../access/AccessControl/_category_.json | 10 + .../AccessControlPausable/_category_.json | 10 + .../AccessControlTemporal/_category_.json | 10 + .../contracts/access/Owner/_category_.json | 10 + .../access/OwnerTwoSteps/_category_.json | 10 + website/docs/contracts/access/_category_.json | 10 + .../docs/contracts/diamond/_category_.json | 10 + .../contracts/diamond/example/_category_.json | 10 + .../contracts/facets/AccessControlFacet.mdx | 547 ------------- .../facets/AccessControlPausableFacet.mdx | 375 --------- .../facets/AccessControlTemporalFacet.mdx | 467 ----------- .../docs/contracts/facets/DiamondCutFacet.mdx | 371 --------- .../contracts/facets/DiamondLoupeFacet.mdx | 255 ------ .../docs/contracts/facets/ERC1155Facet.mdx | 699 ---------------- .../contracts/facets/ERC20BridgeableFacet.mdx | 434 ---------- .../docs/contracts/facets/ERC20BurnFacet.mdx | 252 ------ website/docs/contracts/facets/ERC20Facet.mdx | 578 -------------- .../contracts/facets/ERC20PermitFacet.mdx | 334 -------- .../docs/contracts/facets/ERC6909Facet.mdx | 530 ------------ .../docs/contracts/facets/ERC721BurnFacet.mdx | 209 ----- .../facets/ERC721EnumerableBurnFacet.mdx | 222 ------ .../facets/ERC721EnumerableFacet.mdx | 753 ------------------ website/docs/contracts/facets/ERC721Facet.mdx | 663 --------------- .../docs/contracts/facets/ExampleDiamond.mdx | 129 --- website/docs/contracts/facets/OwnerFacet.mdx | 216 ----- .../contracts/facets/OwnerTwoStepsFacet.mdx | 292 ------- .../docs/contracts/facets/RoyaltyFacet.mdx | 195 ----- website/docs/contracts/facets/_category_.json | 6 - .../interfaceDetection/ERC165/_category_.json | 10 + .../interfaceDetection/_category_.json | 10 + .../docs/contracts/libraries/_category_.json | 10 + .../contracts/modules/AccessControlMod.mdx | 450 ----------- .../modules/AccessControlPausableMod.mdx | 388 --------- .../modules/AccessControlTemporalMod.mdx | 479 ----------- .../docs/contracts/modules/DiamondCutMod.mdx | 414 ---------- website/docs/contracts/modules/DiamondMod.mdx | 234 ------ website/docs/contracts/modules/ERC1155Mod.mdx | 618 -------------- website/docs/contracts/modules/ERC165Mod.mdx | 155 ---- .../contracts/modules/ERC20BridgeableMod.mdx | 424 ---------- website/docs/contracts/modules/ERC20Mod.mdx | 424 ---------- .../docs/contracts/modules/ERC20PermitMod.mdx | 282 ------- website/docs/contracts/modules/ERC6909Mod.mdx | 528 ------------ .../contracts/modules/ERC721EnumerableMod.mdx | 347 -------- website/docs/contracts/modules/ERC721Mod.mdx | 354 -------- .../contracts/modules/NonReentrancyMod.mdx | 142 ---- website/docs/contracts/modules/OwnerMod.mdx | 253 ------ .../contracts/modules/OwnerTwoStepsMod.mdx | 318 -------- website/docs/contracts/modules/RoyaltyMod.mdx | 364 --------- .../docs/contracts/modules/_category_.json | 6 - .../contracts/token/ERC1155/_category_.json | 10 + .../token/ERC20/ERC20/_category_.json | 10 + .../ERC20/ERC20Bridgeable/_category_.json | 10 + .../token/ERC20/ERC20Permit/_category_.json | 10 + .../contracts/token/ERC20/_category_.json | 10 + .../token/ERC6909/ERC6909/_category_.json | 10 + .../contracts/token/ERC6909/_category_.json | 10 + .../token/ERC721/ERC721/_category_.json | 10 + .../ERC721/ERC721Enumerable/_category_.json | 10 + .../contracts/token/ERC721/_category_.json | 10 + .../contracts/token/Royalty/_category_.json | 10 + website/docs/contracts/token/_category_.json | 10 + 69 files changed, 1361 insertions(+), 13965 deletions(-) create mode 100644 .github/scripts/generate-docs-utils/category-generator.js create mode 100644 .github/scripts/sync-docs-structure.js create mode 100644 website/docs/contracts/access/AccessControl/_category_.json create mode 100644 website/docs/contracts/access/AccessControlPausable/_category_.json create mode 100644 website/docs/contracts/access/AccessControlTemporal/_category_.json create mode 100644 website/docs/contracts/access/Owner/_category_.json create mode 100644 website/docs/contracts/access/OwnerTwoSteps/_category_.json create mode 100644 website/docs/contracts/access/_category_.json create mode 100644 website/docs/contracts/diamond/_category_.json create mode 100644 website/docs/contracts/diamond/example/_category_.json delete mode 100644 website/docs/contracts/facets/AccessControlFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlPausableFacet.mdx delete mode 100644 website/docs/contracts/facets/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondCutFacet.mdx delete mode 100644 website/docs/contracts/facets/DiamondLoupeFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC1155Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC20Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC20PermitFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC6909Facet.mdx delete mode 100644 website/docs/contracts/facets/ERC721BurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/contracts/facets/ERC721Facet.mdx delete mode 100644 website/docs/contracts/facets/ExampleDiamond.mdx delete mode 100644 website/docs/contracts/facets/OwnerFacet.mdx delete mode 100644 website/docs/contracts/facets/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/contracts/facets/RoyaltyFacet.mdx delete mode 100644 website/docs/contracts/facets/_category_.json create mode 100644 website/docs/contracts/interfaceDetection/ERC165/_category_.json create mode 100644 website/docs/contracts/interfaceDetection/_category_.json create mode 100644 website/docs/contracts/libraries/_category_.json delete mode 100644 website/docs/contracts/modules/AccessControlMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlPausableMod.mdx delete mode 100644 website/docs/contracts/modules/AccessControlTemporalMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondCutMod.mdx delete mode 100644 website/docs/contracts/modules/DiamondMod.mdx delete mode 100644 website/docs/contracts/modules/ERC1155Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC165Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20BridgeableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC20Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC20PermitMod.mdx delete mode 100644 website/docs/contracts/modules/ERC6909Mod.mdx delete mode 100644 website/docs/contracts/modules/ERC721EnumerableMod.mdx delete mode 100644 website/docs/contracts/modules/ERC721Mod.mdx delete mode 100644 website/docs/contracts/modules/NonReentrancyMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerMod.mdx delete mode 100644 website/docs/contracts/modules/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/contracts/modules/RoyaltyMod.mdx delete mode 100644 website/docs/contracts/modules/_category_.json create mode 100644 website/docs/contracts/token/ERC1155/_category_.json create mode 100644 website/docs/contracts/token/ERC20/ERC20/_category_.json create mode 100644 website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json create mode 100644 website/docs/contracts/token/ERC20/ERC20Permit/_category_.json create mode 100644 website/docs/contracts/token/ERC20/_category_.json create mode 100644 website/docs/contracts/token/ERC6909/ERC6909/_category_.json create mode 100644 website/docs/contracts/token/ERC6909/_category_.json create mode 100644 website/docs/contracts/token/ERC721/ERC721/_category_.json create mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json create mode 100644 website/docs/contracts/token/ERC721/_category_.json create mode 100644 website/docs/contracts/token/Royalty/_category_.json create mode 100644 website/docs/contracts/token/_category_.json diff --git a/.github/docs-gen-prompts.md b/.github/docs-gen-prompts.md index 5b057b16..ca24b27c 100644 --- a/.github/docs-gen-prompts.md +++ b/.github/docs-gen-prompts.md @@ -42,21 +42,25 @@ These section headers from `copilot-instructions.md` are appended to the system Given this module documentation from the Compose diamond proxy framework, enhance it by generating developer-grade content that is specific, actionable, and faithful to the provided contract data. -1. **overview**: 2-3 sentence overview of what the module does and why it matters for diamonds (storage reuse, composition, safety). -2. **usageExample**: 10-20 lines of Solidity demonstrating how a facet would import and call this module. Use the real function names and signatures; include pragma and any required imports. Keep it minimal but compilable. -3. **bestPractices**: 2-3 bullets focused on safe and idiomatic use (access control, storage hygiene, upgrade awareness, error handling). -4. **integrationNotes**: Explain how the module interacts with diamond storage and how changes are visible to facets; note any invariants or ordering requirements. -5. **keyFeatures**: 2-4 bullets highlighting unique capabilities, constraints, or guarantees. +1. **description**: A concise one-line description (max 100 chars) for the page subtitle. Derive from the module's purpose based on its functions and NatSpec. Do NOT include "module" or "for Compose diamonds" - just describe what it does. +2. **overview**: 2-3 sentence overview of what the module does and why it matters for diamonds (storage reuse, composition, safety). +3. **usageExample**: 10-20 lines of Solidity demonstrating how a facet would import and call this module. Use the real function names and signatures; include pragma and any required imports. Keep it minimal but compilable. +4. **bestPractices**: 2-3 bullets focused on safe and idiomatic use (access control, storage hygiene, upgrade awareness, error handling). +5. **integrationNotes**: Explain how the module interacts with diamond storage and how changes are visible to facets; note any invariants or ordering requirements. +6. **keyFeatures**: 2-4 bullets highlighting unique capabilities, constraints, or guarantees. Contract Information: - Name: {{title}} -- Description: {{description}} +- Current Description: {{description}} - Functions: {{functionNames}} +- Events: {{eventNames}} +- Errors: {{errorNames}} - Function Details: {{functionDescriptions}} Respond ONLY with valid JSON in this exact format (no markdown code blocks, no extra text): { + "description": "concise one-line description here", "overview": "enhanced overview text here", "usageExample": "solidity code here (use \\n for newlines)", "bestPractices": "- Point 1\\n- Point 2\\n- Point 3", @@ -70,21 +74,25 @@ Respond ONLY with valid JSON in this exact format (no markdown code blocks, no e Given this facet documentation from the Compose diamond proxy framework, enhance it by generating precise, implementation-ready guidance. -1. **overview**: 2-3 sentence summary of the facet’s purpose and value inside a diamond (routing, orchestration, surface area). -2. **usageExample**: 10-20 lines showing how this facet is deployed or invoked within a diamond. Include pragma, imports, selector usage, and sample calls that reflect the real function names and signatures. -3. **bestPractices**: 2-3 bullets on correct integration patterns (initialization, access control, storage handling, upgrade safety). -4. **securityConsiderations**: Concise notes on access control, reentrancy, input validation, and any state-coupling risks specific to this facet. -5. **keyFeatures**: 2-4 bullets calling out unique abilities, constraints, or guarantees. +1. **description**: A concise one-line description (max 100 chars) for the page subtitle. Derive from the facet's purpose based on its functions and NatSpec. Do NOT include "facet" or "for Compose diamonds" - just describe what it does. +2. **overview**: 2-3 sentence summary of the facet's purpose and value inside a diamond (routing, orchestration, surface area). +3. **usageExample**: 10-20 lines showing how this facet is deployed or invoked within a diamond. Include pragma, imports, selector usage, and sample calls that reflect the real function names and signatures. +4. **bestPractices**: 2-3 bullets on correct integration patterns (initialization, access control, storage handling, upgrade safety). +5. **securityConsiderations**: Concise notes on access control, reentrancy, input validation, and any state-coupling risks specific to this facet. +6. **keyFeatures**: 2-4 bullets calling out unique abilities, constraints, or guarantees. Contract Information: - Name: {{title}} -- Description: {{description}} +- Current Description: {{description}} - Functions: {{functionNames}} +- Events: {{eventNames}} +- Errors: {{errorNames}} - Function Details: {{functionDescriptions}} Respond ONLY with valid JSON in this exact format (no markdown code blocks, no extra text): { + "description": "concise one-line description here", "overview": "enhanced overview text here", "usageExample": "solidity code here (use \\n for newlines)", "bestPractices": "- Point 1\\n- Point 2\\n- Point 3", diff --git a/.github/scripts/ai-provider/rate-limiter.js b/.github/scripts/ai-provider/rate-limiter.js index df1d9c3f..410490c4 100644 --- a/.github/scripts/ai-provider/rate-limiter.js +++ b/.github/scripts/ai-provider/rate-limiter.js @@ -60,17 +60,11 @@ class RateLimiter { const waitTime = this._calculateTokenWaitTime(estimatedTokens, currentConsumption); if (waitTime > 0) { console.log( - ` ⏳ Token budget: ${currentConsumption.toFixed(0)}/${effectiveBudget.toFixed(0)} used. ` + `Waiting ${Math.ceil(waitTime / 1000)}s...` ); await this._sleep(waitTime); this._cleanTokenHistory(); } - } else { - console.log( - ` 📊 Token budget: ${currentConsumption.toFixed(0)}/${effectiveBudget.toFixed(0)} used, ` + - `~${estimatedTokens} needed` - ); } this.lastCallTime = Date.now(); diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index 793e684d..b688ec18 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -150,6 +150,10 @@ function buildPrompt(data, contractType) { .map(f => `- ${f.name}: ${f.description || 'No description'}`) .join('\n'); + // Include events and errors for richer context + const eventNames = (data.events || []).map(e => e.name).join(', '); + const errorNames = (data.errors || []).map(e => e.name).join(', '); + const promptTemplate = contractType === 'module' ? AI_PROMPTS.modulePrompt : AI_PROMPTS.facetPrompt; @@ -160,33 +164,40 @@ function buildPrompt(data, contractType) { .replace(/\{\{title\}\}/g, data.title) .replace(/\{\{description\}\}/g, data.description || 'No description provided') .replace(/\{\{functionNames\}\}/g, functionNames || 'None') - .replace(/\{\{functionDescriptions\}\}/g, functionDescriptions || ' None'); + .replace(/\{\{functionDescriptions\}\}/g, functionDescriptions || ' None') + .replace(/\{\{eventNames\}\}/g, eventNames || 'None') + .replace(/\{\{errorNames\}\}/g, errorNames || 'None'); } // Fallback to hardcoded prompt if template not loaded return `Given this ${contractType} documentation from the Compose diamond proxy framework, enhance it by generating: -1. **overview**: A clear, concise overview (2-3 sentences) explaining what this ${contractType} does and why it's useful in the context of diamond contracts. +1. **description**: A concise one-line description (max 100 chars) for the page subtitle. Derive this from the contract's purpose based on its functions, events, and errors. + +2. **overview**: A clear, concise overview (2-3 sentences) explaining what this ${contractType} does and why it's useful in the context of diamond contracts. -2. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this ${contractType}. For modules, show importing and calling functions. For facets, show how it would be used in a diamond. +3. **usageExample**: A practical Solidity code example (10-20 lines) showing how to use this ${contractType}. For modules, show importing and calling functions. For facets, show how it would be used in a diamond. -3. **bestPractices**: 2-3 bullet points of best practices for using this ${contractType}. +4. **bestPractices**: 2-3 bullet points of best practices for using this ${contractType}. -${contractType === 'module' ? '4. **integrationNotes**: A note about how this module works with diamond storage pattern and how changes made through it are visible to facets.' : ''} +${contractType === 'module' ? '5. **integrationNotes**: A note about how this module works with diamond storage pattern and how changes made through it are visible to facets.' : ''} -${contractType === 'facet' ? '4. **securityConsiderations**: Important security considerations when using this facet (access control, reentrancy, etc.).' : ''} +${contractType === 'facet' ? '5. **securityConsiderations**: Important security considerations when using this facet (access control, reentrancy, etc.).' : ''} -5. **keyFeatures**: A brief bullet list of key features. +6. **keyFeatures**: A brief bullet list of key features. Contract Information: - Name: ${data.title} -- Description: ${data.description || 'No description provided'} +- Current Description: ${data.description || 'No description provided'} - Functions: ${functionNames || 'None'} +- Events: ${eventNames || 'None'} +- Errors: ${errorNames || 'None'} - Function Details: ${functionDescriptions || ' None'} Respond ONLY with valid JSON in this exact format (no markdown code blocks, no extra text): { + "description": "concise one-line description here", "overview": "enhanced overview text here", "usageExample": "solidity code here (use \\n for newlines)", "bestPractices": "- Point 1\\n- Point 2\\n- Point 3", @@ -220,9 +231,16 @@ function convertEnhancedFields(enhanced, data) { .replace(/'/g, "'") .replace(/&/g, '&'); }; + + // Use AI-generated description if provided, otherwise keep original + const aiDescription = enhanced.description?.trim(); + const finalDescription = aiDescription || data.description; return { ...data, + // Description is used for page subtitle - AI improves it from NatSpec + description: finalDescription, + subtitle: finalDescription, overview: convertNewlines(enhanced.overview) || data.overview, usageExample: decodeHtmlEntities(convertNewlines(enhanced.usageExample)) || null, bestPractices: convertNewlines(enhanced.bestPractices) || null, diff --git a/.github/scripts/generate-docs-utils/category-generator.js b/.github/scripts/generate-docs-utils/category-generator.js new file mode 100644 index 00000000..b2cfcd2c --- /dev/null +++ b/.github/scripts/generate-docs-utils/category-generator.js @@ -0,0 +1,484 @@ +/** + * Category Generator + * + * Automatically generates _category_.json files to mirror + * the src/ folder structure in the documentation. + * + * This module provides: + * - Source structure scanning + * - Category file generation + * - Path computation for doc output + * - Structure synchronization + */ + +const fs = require('fs'); +const path = require('path'); +const CONFIG = require('./config'); + +// ============================================================================ +// Constants +// ============================================================================ + +/** + * Human-readable labels for directory names + * Add new entries here when adding new top-level categories + */ +const CATEGORY_LABELS = { + // Top-level categories + access: 'Access Control', + token: 'Token Standards', + diamond: 'Diamond Core', + libraries: 'Utilities', + interfaceDetection: 'Interface Detection', + + // Token subcategories + ERC20: 'ERC-20', + ERC721: 'ERC-721', + ERC1155: 'ERC-1155', + ERC6909: 'ERC-6909', + Royalty: 'Royalty', + + // Access subcategories + AccessControl: 'Access Control', + AccessControlPausable: 'Pausable Access Control', + AccessControlTemporal: 'Temporal Access Control', + Owner: 'Owner', + OwnerTwoSteps: 'Two-Step Owner', +}; + +/** + * Descriptions for categories + * Add new entries here for custom descriptions + */ +const CATEGORY_DESCRIPTIONS = { + // Top-level categories + access: 'Access control patterns for permission management in Compose diamonds.', + token: 'Token standard implementations for Compose diamonds.', + diamond: 'Core diamond proxy functionality for ERC-2535 diamonds.', + libraries: 'Utility libraries and helpers for diamond development.', + interfaceDetection: 'ERC-165 interface detection support.', + + // Token subcategories + ERC20: 'ERC-20 fungible token implementations.', + ERC721: 'ERC-721 non-fungible token implementations.', + ERC1155: 'ERC-1155 multi-token implementations.', + ERC6909: 'ERC-6909 minimal multi-token implementations.', + Royalty: 'ERC-2981 royalty standard implementations.', + + // Access subcategories + AccessControl: 'Role-based access control (RBAC) pattern.', + AccessControlPausable: 'RBAC with pause functionality.', + AccessControlTemporal: 'Time-limited role-based access control.', + Owner: 'Single-owner access control pattern.', + OwnerTwoSteps: 'Two-step ownership transfer pattern.', +}; + +/** + * Sidebar positions for categories + * Lower numbers appear first in the sidebar + */ +const CATEGORY_POSITIONS = { + // Top-level (lower = higher priority) + diamond: 1, + access: 2, + token: 3, + libraries: 4, + interfaceDetection: 5, + + // Token subcategories + ERC20: 1, + ERC721: 2, + ERC1155: 3, + ERC6909: 4, + Royalty: 5, + + // Access subcategories + Owner: 1, + OwnerTwoSteps: 2, + AccessControl: 3, + AccessControlPausable: 4, + AccessControlTemporal: 5, + + // Leaf directories (ERC20/ERC20, etc.) - alphabetical + ERC20Bridgeable: 2, + ERC20Permit: 3, + ERC721Enumerable: 2, +}; + +// ============================================================================ +// Label & Description Generation +// ============================================================================ + +/** + * Generate a human-readable label from a directory name + * @param {string} name - Directory name (e.g., 'AccessControlPausable', 'ERC20') + * @returns {string} Human-readable label + */ +function generateLabel(name) { + // Check explicit mapping first + if (CATEGORY_LABELS[name]) { + return CATEGORY_LABELS[name]; + } + + // Handle ERC standards specially + if (/^ERC\d+/.test(name)) { + const match = name.match(/^(ERC)(\d+)(.*)$/); + if (match) { + const variant = match[3] + ? ' ' + match[3].replace(/([A-Z])/g, ' $1').trim() + : ''; + return `ERC-${match[2]}${variant}`; + } + return name; + } + + // CamelCase to Title Case with spaces + return name.replace(/([A-Z])/g, ' $1').replace(/^ /, '').trim(); +} + +/** + * Generate description for a category based on its path + * @param {string} name - Directory name + * @param {string[]} parentPath - Parent path segments + * @returns {string} Category description + */ +function generateDescription(name, parentPath = []) { + // Check explicit mapping first + if (CATEGORY_DESCRIPTIONS[name]) { + return CATEGORY_DESCRIPTIONS[name]; + } + + // Generate from context + const label = generateLabel(name); + const parent = parentPath[parentPath.length - 1]; + + if (parent === 'token') { + return `${label} token implementations with modules and facets.`; + } + if (parent === 'access') { + return `${label} access control pattern for Compose diamonds.`; + } + if (parent === 'ERC20' || parent === 'ERC721') { + return `${label} extension for ${generateLabel(parent)} tokens.`; + } + + return `${label} components for Compose diamonds.`; +} + +/** + * Get sidebar position for a category + * @param {string} name - Directory name + * @param {number} depth - Nesting depth + * @returns {number} Sidebar position + */ +function getCategoryPosition(name, depth) { + if (CATEGORY_POSITIONS[name] !== undefined) { + return CATEGORY_POSITIONS[name]; + } + return 99; // Default to end +} + +// ============================================================================ +// Source Structure Scanning +// ============================================================================ + +/** + * Check if a directory contains .sol files (directly or in subdirectories) + * @param {string} dirPath - Directory path to check + * @returns {boolean} True if contains .sol files + */ +function containsSolFiles(dirPath) { + try { + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.sol')) { + return true; + } + if (entry.isDirectory() && !entry.name.startsWith('.')) { + if (containsSolFiles(path.join(dirPath, entry.name))) { + return true; + } + } + } + } catch (error) { + console.warn(`Warning: Could not read directory ${dirPath}: ${error.message}`); + } + + return false; +} + +/** + * Scan the src/ directory and build structure map + * @returns {Map} Map of relative paths to category info + */ +function scanSourceStructure() { + const srcDir = CONFIG.srcDir || 'src'; + const structure = new Map(); + + function scanDir(dirPath, relativePath = '') { + let entries; + try { + entries = fs.readdirSync(dirPath, { withFileTypes: true }); + } catch (error) { + console.error(`Error reading directory ${dirPath}: ${error.message}`); + return; + } + + for (const entry of entries) { + if (!entry.isDirectory()) continue; + + // Skip hidden directories and interfaces + if (entry.name.startsWith('.') || entry.name === 'interfaces') { + continue; + } + + const fullPath = path.join(dirPath, entry.name); + const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name; + + // Only include directories that contain .sol files + if (containsSolFiles(fullPath)) { + const parts = relPath.split('/'); + structure.set(relPath, { + name: entry.name, + path: relPath, + depth: parts.length, + parent: relativePath || null, + parentParts: relativePath ? relativePath.split('/') : [], + }); + + // Recurse into subdirectories + scanDir(fullPath, relPath); + } + } + } + + if (fs.existsSync(srcDir)) { + scanDir(srcDir); + } else { + console.warn(`Warning: Source directory ${srcDir} does not exist`); + } + + return structure; +} + +// ============================================================================ +// Category File Generation +// ============================================================================ + +/** + * Create a _category_.json file for a directory + * @param {string} outputDir - Directory to create category file in + * @param {string} name - Directory name + * @param {string} relativePath - Relative path from contracts dir + * @param {number} depth - Nesting depth + * @returns {boolean} True if file was created, false if it already existed + */ +function createCategoryFile(outputDir, name, relativePath, depth) { + const categoryFile = path.join(outputDir, '_category_.json'); + + // Don't overwrite existing category files (allows manual customization) + if (fs.existsSync(categoryFile)) { + return false; + } + + const parentParts = relativePath.split('/').slice(0, -1); + const label = generateLabel(name); + const position = getCategoryPosition(name, depth); + const description = generateDescription(name, parentParts); + + const category = { + label, + position, + collapsible: true, + collapsed: depth > 1, // Collapse nested categories by default + link: { + type: 'generated-index', + description, + }, + }; + + // Ensure directory exists + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(categoryFile, JSON.stringify(category, null, 2) + '\n'); + + return true; +} + +/** + * Ensure the base contracts category file exists + * @param {string} contractsDir - Path to contracts directory + * @returns {boolean} True if created, false if existed + */ +function ensureBaseCategory(contractsDir) { + const categoryFile = path.join(contractsDir, '_category_.json'); + + if (fs.existsSync(categoryFile)) { + return false; + } + + const baseCategory = { + label: 'Contracts', + position: 4, + collapsible: true, + collapsed: false, + link: { + type: 'generated-index', + title: 'Contract Reference', + description: 'API reference for all Compose modules and facets.', + }, + }; + + fs.mkdirSync(contractsDir, { recursive: true }); + fs.writeFileSync(categoryFile, JSON.stringify(baseCategory, null, 2) + '\n'); + + return true; +} + +// ============================================================================ +// Path Computation +// ============================================================================ + +/** + * Compute output path for a source file + * Mirrors the src/ structure in website/docs/contracts/ + * + * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/AccessControlMod.sol') + * @returns {object} Output path information + */ +function computeOutputPath(solFilePath) { + const contractsDir = CONFIG.contractsOutputDir || 'website/docs/contracts'; + + // Normalize path separators + const normalizedPath = solFilePath.replace(/\\/g, '/'); + + // Remove 'src/' prefix and '.sol' extension + const relativePath = normalizedPath.replace(/^src\//, '').replace(/\.sol$/, ''); + + const parts = relativePath.split('/'); + const fileName = parts.pop(); + + const outputDir = path.join(contractsDir, ...parts); + const outputFile = path.join(outputDir, `${fileName}.mdx`); + + return { + outputDir, + outputFile, + relativePath: parts.join('/'), + fileName, + category: parts[0] || '', + subcategory: parts[1] || '', + fullRelativePath: relativePath, + depth: parts.length, + }; +} + +/** + * Ensure all parent category files exist for a given output path + * Creates _category_.json files for each directory level + * + * @param {string} outputDir - Full output directory path + */ +function ensureCategoryFiles(outputDir) { + const contractsDir = CONFIG.contractsOutputDir || 'website/docs/contracts'; + + // Get relative path from contracts base + const relativePath = path.relative(contractsDir, outputDir); + + if (!relativePath || relativePath.startsWith('..')) { + return; // outputDir is not under contractsDir + } + + // Ensure base category exists + ensureBaseCategory(contractsDir); + + // Walk up the directory tree, creating category files + const parts = relativePath.split(path.sep); + let currentPath = contractsDir; + + for (let i = 0; i < parts.length; i++) { + currentPath = path.join(currentPath, parts[i]); + const segment = parts[i]; + const relPath = parts.slice(0, i + 1).join('/'); + + createCategoryFile(currentPath, segment, relPath, i + 1); + } +} + +// ============================================================================ +// Structure Synchronization +// ============================================================================ + +/** + * Synchronize docs structure with src structure + * Creates any missing category directories and _category_.json files + * + * @returns {object} Summary of created categories + */ +function syncDocsStructure() { + const structure = scanSourceStructure(); + const contractsDir = CONFIG.contractsOutputDir || 'website/docs/contracts'; + + const created = []; + const existing = []; + + // Ensure base contracts directory exists with category + if (ensureBaseCategory(contractsDir)) { + created.push('contracts'); + } else { + existing.push('contracts'); + } + + // Create category for each directory in the structure + // Sort by path to ensure parents are created before children + const sortedPaths = Array.from(structure.entries()).sort((a, b) => + a[0].localeCompare(b[0]) + ); + + for (const [relativePath, info] of sortedPaths) { + const outputDir = path.join(contractsDir, relativePath); + const wasCreated = createCategoryFile( + outputDir, + info.name, + relativePath, + info.depth + ); + + if (wasCreated) { + created.push(relativePath); + } else { + existing.push(relativePath); + } + } + + return { + created, + existing, + total: structure.size, + structure, + }; +} + +// ============================================================================ +// Exports +// ============================================================================ + +module.exports = { + // Core functions + scanSourceStructure, + syncDocsStructure, + computeOutputPath, + ensureCategoryFiles, + + // Utilities + generateLabel, + generateDescription, + getCategoryPosition, + containsSolFiles, + + // For extending/customizing + CATEGORY_LABELS, + CATEGORY_DESCRIPTIONS, + CATEGORY_POSITIONS, +}; + diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index b8aaa673..814b9f88 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -1,18 +1,111 @@ /** * Configuration for documentation generation - * + * * Centralized configuration for paths, settings, and defaults. * Modify this file to change documentation output paths or behavior. */ module.exports = { - // Input paths + // ============================================================================ + // Input Paths + // ============================================================================ + + /** Directory containing forge doc output */ forgeDocsDir: 'docs/src/src', - - // Output paths for generated documentation - facetsOutputDir: 'website/docs/contracts/facets', - modulesOutputDir: 'website/docs/contracts/modules', - - // Template settings - defaultSidebarPosition: 99 + + /** Source code directory to mirror */ + srcDir: 'src', + + // ============================================================================ + // Output Paths + // ============================================================================ + + /** + * Base output directory for contract documentation + * Structure mirrors src/ automatically + */ + contractsOutputDir: 'website/docs/contracts', + + // ============================================================================ + // Sidebar Positions + // ============================================================================ + + /** Default sidebar position for contracts without explicit mapping */ + defaultSidebarPosition: 50, + + /** + * Contract-specific sidebar positions + * Maps contract name to position number (lower = higher in sidebar) + * + * Convention: + * - Modules come before their corresponding facets + * - Core/base contracts come before extensions + * - Burn facets come after main facets + */ + contractPositions: { + // Diamond core + DiamondMod: 1, + DiamondCutMod: 2, + DiamondCutFacet: 3, + DiamondLoupeFacet: 4, + + // Access - Owner pattern + OwnerMod: 1, + OwnerFacet: 2, + + // Access - Two-step owner + OwnerTwoStepsMod: 1, + OwnerTwoStepsFacet: 2, + + // Access - AccessControl pattern + AccessControlMod: 1, + AccessControlFacet: 2, + + // Access - AccessControlPausable + AccessControlPausableMod: 1, + AccessControlPausableFacet: 2, + + // Access - AccessControlTemporal + AccessControlTemporalMod: 1, + AccessControlTemporalFacet: 2, + + // ERC-20 base + ERC20Mod: 1, + ERC20Facet: 2, + ERC20BurnFacet: 3, + + // ERC-20 Bridgeable + ERC20BridgeableMod: 1, + ERC20BridgeableFacet: 2, + + // ERC-20 Permit + ERC20PermitMod: 1, + ERC20PermitFacet: 2, + + // ERC-721 base + ERC721Mod: 1, + ERC721Facet: 2, + ERC721BurnFacet: 3, + + // ERC-721 Enumerable + ERC721EnumerableMod: 1, + ERC721EnumerableFacet: 2, + ERC721EnumerableBurnFacet: 3, + + // ERC-1155 + ERC1155Mod: 1, + ERC1155Facet: 2, + + // ERC-6909 + ERC6909Mod: 1, + ERC6909Facet: 2, + + // Royalty + RoyaltyMod: 1, + RoyaltyFacet: 2, + + // Libraries + NonReentrancyMod: 1, + ERC165Mod: 1, + }, }; diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js index 75f08453..c1977977 100644 --- a/.github/scripts/generate-docs-utils/doc-generation-utils.js +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -1,8 +1,26 @@ +/** + * Documentation Generation Utilities + * + * Provides helper functions for: + * - Finding and reading Solidity source files + * - Detecting contract types (module vs facet) + * - Computing output paths (mirrors src/ structure) + * - Extracting documentation from source files + */ + const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const { readFileSafe } = require('../workflow-utils'); const CONFIG = require('./config'); +const { + computeOutputPath, + ensureCategoryFiles, +} = require('./category-generator'); + +// ============================================================================ +// Git Integration +// ============================================================================ /** * Get list of changed Solidity files from git diff @@ -14,7 +32,10 @@ function getChangedSolFiles(baseBranch = 'HEAD~1') { const output = execSync(`git diff --name-only ${baseBranch} HEAD -- 'src/**/*.sol'`, { encoding: 'utf8', }); - return output.trim().split('\n').filter(f => f.endsWith('.sol')); + return output + .trim() + .split('\n') + .filter((f) => f.endsWith('.sol')); } catch (error) { console.error('Error getting changed files:', error.message); return []; @@ -30,21 +51,44 @@ function getAllSolFiles() { const output = execSync('find src -name "*.sol" -type f', { encoding: 'utf8', }); - return output.trim().split('\n').filter(f => f); + return output + .trim() + .split('\n') + .filter((f) => f); } catch (error) { console.error('Error getting all sol files:', error.message); return []; } } +/** + * Read changed files from a file (used in CI) + * @param {string} filePath - Path to file containing list of changed files + * @returns {string[]} Array of file paths + */ +function readChangedFilesFromFile(filePath) { + const content = readFileSafe(filePath); + if (!content) { + return []; + } + return content + .trim() + .split('\n') + .filter((f) => f.endsWith('.sol')); +} + +// ============================================================================ +// Forge Doc Integration +// ============================================================================ + /** * Find forge doc output files for a given source file - * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/LibAccessControl.sol') + * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/AccessControlMod.sol') * @returns {string[]} Array of markdown file paths from forge doc output */ function findForgeDocFiles(solFilePath) { - // Transform: src/access/AccessControl/LibAccessControl.sol - // To: docs/src/src/access/AccessControl/LibAccessControl.sol/ + // Transform: src/access/AccessControl/AccessControlMod.sol + // To: docs/src/src/access/AccessControl/AccessControlMod.sol/ const relativePath = solFilePath.replace(/^src\//, ''); const docsDir = path.join(CONFIG.forgeDocsDir, relativePath); @@ -54,15 +98,17 @@ function findForgeDocFiles(solFilePath) { try { const files = fs.readdirSync(docsDir); - return files - .filter(f => f.endsWith('.md')) - .map(f => path.join(docsDir, f)); + return files.filter((f) => f.endsWith('.md')).map((f) => path.join(docsDir, f)); } catch (error) { console.error(`Error reading docs dir ${docsDir}:`, error.message); return []; } } +// ============================================================================ +// Contract Type Detection +// ============================================================================ + /** * Determine if a contract is an interface * Interfaces should be skipped from documentation generation @@ -75,27 +121,96 @@ function isInterface(title, content) { if (title && /^I[A-Z]/.test(title)) { return true; } - + // Check if content indicates it's an interface - // Forge doc marks interfaces with "interface" in the first few lines if (content) { const firstLines = content.split('\n').slice(0, 20).join('\n').toLowerCase(); if (firstLines.includes('interface ') || firstLines.includes('*interface*')) { return true; } } - + return false; } +/** + * Determine if a contract is a module or facet + * @param {string} filePath - Path to the file + * @param {string} content - File content + * @returns {'module' | 'facet'} Contract type + */ +function getContractType(filePath, content) { + const lowerPath = filePath.toLowerCase(); + const normalizedPath = lowerPath.replace(/\\/g, '/'); + const baseName = path.basename(filePath, path.extname(filePath)).toLowerCase(); + + // Explicit modules folder + if (normalizedPath.includes('/modules/')) { + return 'module'; + } + + // File naming conventions (e.g., AccessControlMod.sol, NonReentrancyModule.sol) + if (baseName.endsWith('mod') || baseName.endsWith('module')) { + return 'module'; + } + + if (lowerPath.includes('facet')) { + return 'facet'; + } + + // Libraries folder typically contains modules + if (normalizedPath.includes('/libraries/')) { + return 'module'; + } + + // Default to facet for contracts + return 'facet'; +} + +// ============================================================================ +// Output Path Computation +// ============================================================================ + +/** + * Get output directory and file path based on source file path + * Mirrors the src/ structure in website/docs/contracts/ + * + * @param {string} solFilePath - Path to the source .sol file + * @param {'module' | 'facet'} contractType - Type of contract (for logging) + * @returns {object} { outputDir, outputFile, relativePath, fileName, category } + */ +function getOutputPath(solFilePath, contractType) { + // Compute path using the new structure-mirroring logic + const pathInfo = computeOutputPath(solFilePath); + + // Ensure all parent category files exist + ensureCategoryFiles(pathInfo.outputDir); + + return pathInfo; +} + +/** + * Get sidebar position for a contract + * @param {string} contractName - Name of the contract + * @returns {number} Sidebar position + */ +function getSidebarPosition(contractName) { + if (CONFIG.contractPositions && CONFIG.contractPositions[contractName] !== undefined) { + return CONFIG.contractPositions[contractName]; + } + return CONFIG.defaultSidebarPosition || 50; +} + +// ============================================================================ +// Source File Parsing +// ============================================================================ + /** * Extract module name from file path - * @param {string} filePath - Path to the file (e.g., 'src/modules/LibNonReentrancy.sol' or 'constants.LibNonReentrancy.md') - * @returns {string} Module name (e.g., 'LibNonReentrancy') + * @param {string} filePath - Path to the file + * @returns {string} Module name */ function extractModuleNameFromPath(filePath) { - const path = require('path'); - // If it's a constants file, extract from filename const basename = path.basename(filePath); if (basename.startsWith('constants.')) { @@ -104,26 +219,26 @@ function extractModuleNameFromPath(filePath) { return match[1]; } } - + // Extract from .sol file path if (filePath.endsWith('.sol')) { return path.basename(filePath, '.sol'); } - - // Extract from directory structure (e.g., docs/src/src/libraries/LibNonReentrancy.sol/function.enter.md) + + // Extract from directory structure const parts = filePath.split(path.sep); for (let i = parts.length - 1; i >= 0; i--) { if (parts[i].endsWith('.sol')) { return path.basename(parts[i], '.sol'); } } - + // Fallback: use basename without extension return path.basename(filePath, path.extname(filePath)); } /** - * Check if a line is a code element declaration (event, error, function, struct, etc.) + * Check if a line is a code element declaration * @param {string} line - Trimmed line to check * @returns {boolean} True if line is a code element declaration */ @@ -146,10 +261,8 @@ function isCodeElementDeclaration(line) { /** * Extract module description from source file NatSpec comments - * Only extracts TRUE file-level comments (those with @title, or comments not immediately followed by code elements) - * Skips comments that belong to events, errors, functions, etc. * @param {string} solFilePath - Path to the Solidity source file - * @returns {string} Description extracted from @title and @notice tags, or empty string + * @returns {string} Description extracted from @title and @notice tags */ function extractModuleDescriptionFromSource(solFilePath) { const content = readFileSafe(solFilePath); @@ -159,7 +272,6 @@ function extractModuleDescriptionFromSource(solFilePath) { const lines = content.split('\n'); let inComment = false; - let commentStartLine = -1; let commentBuffer = []; let title = ''; let notice = ''; @@ -175,14 +287,12 @@ function extractModuleDescriptionFromSource(solFilePath) { // Check if we've reached a code element without finding a file-level comment if (!inComment && isCodeElementDeclaration(trimmed)) { - // We hit code without finding a file-level comment break; } // Start of block comment if (trimmed.startsWith('/**') || trimmed.startsWith('/*')) { inComment = true; - commentStartLine = i; commentBuffer = []; continue; } @@ -191,7 +301,7 @@ function extractModuleDescriptionFromSource(solFilePath) { if (inComment && trimmed.includes('*/')) { inComment = false; const commentText = commentBuffer.join(' '); - + // Look ahead to see if next non-empty line is a code element let nextCodeLine = ''; for (let j = i + 1; j < lines.length && j < i + 5; j++) { @@ -201,7 +311,7 @@ function extractModuleDescriptionFromSource(solFilePath) { break; } } - + // If the comment has @title, it's a file-level comment const titleMatch = commentText.match(/@title\s+(.+?)(?:\s+@|\s*$)/); if (titleMatch) { @@ -210,32 +320,32 @@ function extractModuleDescriptionFromSource(solFilePath) { if (noticeMatch) { notice = noticeMatch[1].trim(); } - break; // Found file-level comment, stop searching + break; } - - // If next line is a code element (event, error, function, etc.), - // this comment belongs to that element, not the file + + // If next line is a code element, this comment belongs to that element if (isCodeElementDeclaration(nextCodeLine)) { - // This is an item-level comment, skip it and continue looking commentBuffer = []; continue; } - - // If it's a standalone comment with @notice (no code element following), use it + + // Standalone comment with @notice const standaloneNotice = commentText.match(/@notice\s+(.+?)(?:\s+@|\s*$)/); if (standaloneNotice && !isCodeElementDeclaration(nextCodeLine)) { notice = standaloneNotice[1].trim(); break; } - + commentBuffer = []; continue; } // Collect comment lines if (inComment) { - // Remove comment markers - let cleanLine = trimmed.replace(/^\*\s*/, '').replace(/^\s*\*/, '').trim(); + let cleanLine = trimmed + .replace(/^\*\s*/, '') + .replace(/^\s*\*/, '') + .trim(); if (cleanLine && !cleanLine.startsWith('*/')) { commentBuffer.push(cleanLine); } @@ -255,158 +365,66 @@ function extractModuleDescriptionFromSource(solFilePath) { } /** - * Generate a meaningful description from module/facet name when no source description exists - * @param {string} contractName - Name of the contract (e.g., "AccessControlMod", "ERC20Facet") - * @returns {string} Generated description + * Generate a fallback description from contract name + * + * This is a minimal, generic fallback used only when: + * 1. No NatSpec @title/@notice exists in source + * 2. AI enhancement will improve it later + * + * The AI enhancement step receives this as input and generates + * a richer, context-aware description from the actual code. + * + * @param {string} contractName - Name of the contract + * @returns {string} Generic description (will be enhanced by AI) */ function generateDescriptionFromName(contractName) { if (!contractName) return ''; - - // Remove common suffixes - let baseName = contractName - .replace(/Mod$/, '') - .replace(/Module$/, '') - .replace(/Facet$/, ''); - - // Add spaces before capitals (CamelCase to spaces) - const readable = baseName - .replace(/([A-Z])/g, ' $1') - .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // Handle acronyms like ERC20 - .trim(); - - // Detect contract type + + // Detect contract type from naming convention const isModule = contractName.endsWith('Mod') || contractName.endsWith('Module'); const isFacet = contractName.endsWith('Facet'); - - // Generate description based on known patterns - const lowerName = baseName.toLowerCase(); - - // Common patterns - if (lowerName.includes('accesscontrol')) { - if (lowerName.includes('pausable')) { - return `Role-based access control with pause functionality for Compose diamonds`; - } - if (lowerName.includes('temporal')) { - return `Time-limited role-based access control for Compose diamonds`; - } - return `Role-based access control (RBAC) ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.startsWith('erc20')) { - const variant = baseName.replace(/^ERC20/, '').trim(); - if (variant) { - return `ERC-20 token ${variant.toLowerCase()} ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - return `ERC-20 fungible token ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.startsWith('erc721')) { - const variant = baseName.replace(/^ERC721/, '').trim(); - if (variant) { - return `ERC-721 NFT ${variant.toLowerCase()} ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - return `ERC-721 non-fungible token ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.startsWith('erc1155')) { - return `ERC-1155 multi-token ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.startsWith('erc6909')) { - return `ERC-6909 minimal multi-token ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.includes('owner')) { - if (lowerName.includes('twostep')) { - return `Two-step ownership transfer ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - return `Ownership management ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.includes('diamond')) { - if (lowerName.includes('cut')) { - return `Diamond upgrade (cut) ${isModule ? 'module' : 'facet'} for ERC-2535 diamonds`; - } - if (lowerName.includes('loupe')) { - return `Diamond introspection (loupe) ${isModule ? 'module' : 'facet'} for ERC-2535 diamonds`; - } - return `Diamond core ${isModule ? 'module' : 'facet'} for ERC-2535 implementation`; - } - if (lowerName.includes('royalty')) { - return `ERC-2981 royalty ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.includes('nonreentran') || lowerName.includes('reentrancy')) { - return `Reentrancy guard ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - if (lowerName.includes('erc165')) { - return `ERC-165 interface detection ${isModule ? 'module' : 'facet'} for Compose diamonds`; - } - - // Generic fallback const typeLabel = isModule ? 'module' : isFacet ? 'facet' : 'contract'; - return `${readable} ${typeLabel} for Compose diamonds`; -} -/** - * Determine if a contract is a module or facet - * Modules are Solidity files whose top-level code lives outside of contracts and Solidity libraries. - * They contain reusable logic that gets pulled into other contracts at compile time. - * @param {string} filePath - Path to the file - * @param {string} content - File content - * @returns {'module' | 'facet'} Contract type - */ -function getContractType(filePath, content) { - const lowerPath = filePath.toLowerCase(); - const normalizedPath = lowerPath.replace(/\\/g, '/'); - const baseName = path.basename(filePath, path.extname(filePath)).toLowerCase(); - - // Explicit modules folder - if (normalizedPath.includes('/modules/')) { - return 'module'; - } + // Remove suffix and convert CamelCase to readable text + const baseName = contractName + .replace(/Mod$/, '') + .replace(/Module$/, '') + .replace(/Facet$/, ''); - // File naming conventions (e.g., AccessControlMod.sol, NonReentrancyModule.sol) - if (baseName.endsWith('mod') || baseName.endsWith('module')) { - return 'module'; - } - - if (lowerPath.includes('facet')) { - return 'facet'; - } - - // Default to facet for contracts - return 'facet'; -} + // Convert CamelCase to readable format + // Handles: ERC20 -> ERC-20, AccessControl -> Access Control + const readable = baseName + .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase splits + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // acronym handling + .replace(/^ERC(\d+)/, 'ERC-$1') // ERC20 -> ERC-20 + .trim(); -/** - * Get output directory based on contract type - * @param {'module' | 'facet'} contractType - Type of contract - * @returns {string} Output directory path - */ -function getOutputDir(contractType) { - return contractType === 'module' - ? CONFIG.modulesOutputDir - : CONFIG.facetsOutputDir; + return `${readable} ${typeLabel} for Compose diamonds`; } -/** - * Read changed files from a file (used in CI) - * @param {string} filePath - Path to file containing list of changed files - * @returns {string[]} Array of file paths - */ -function readChangedFilesFromFile(filePath) { - const content = readFileSafe(filePath); - if (!content) { - return []; - } - return content.trim().split('\n').filter(f => f.endsWith('.sol')); -} +// ============================================================================ +// Exports +// ============================================================================ module.exports = { + // Git integration getChangedSolFiles, getAllSolFiles, + readChangedFilesFromFile, + + // Forge doc integration findForgeDocFiles, + + // Contract type detection isInterface, getContractType, - getOutputDir, - readChangedFilesFromFile, + + // Output path computation + getOutputPath, + getSidebarPosition, + + // Source file parsing extractModuleNameFromPath, extractModuleDescriptionFromSource, generateDescriptionFromName, }; - - diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index f21c7c9e..9099a0c9 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -1,29 +1,36 @@ /** * Docusaurus Documentation Generator - * + * * Converts forge doc output to Docusaurus MDX format - * with optional GitHub Copilot enhancement. - * + * with optional AI enhancement. + * + * Features: + * - Mirrors src/ folder structure in documentation + * - Auto-generates category navigation files + * - AI-enhanced content generation + * * Environment variables: - * GITHUB_TOKEN - GitHub token for Copilot API (optional) - * SKIP_ENHANCEMENT - Set to 'true' to skip Copilot enhancement + * GITHUB_TOKEN - GitHub token for AI API (optional) + * SKIP_ENHANCEMENT - Set to 'true' to skip AI enhancement */ +const fs = require('fs'); const path = require('path'); const { getAllSolFiles, findForgeDocFiles, isInterface, getContractType, - getOutputDir, + getOutputPath, + getSidebarPosition, readChangedFilesFromFile, extractModuleNameFromPath, extractModuleDescriptionFromSource, generateDescriptionFromName, } = require('./generate-docs-utils/doc-generation-utils'); const { readFileSafe, writeFileSafe } = require('./workflow-utils'); -const { - parseForgeDocMarkdown, +const { + parseForgeDocMarkdown, extractStorageInfo, parseIndividualItemFile, aggregateParsedItems, @@ -31,8 +38,13 @@ const { } = require('./generate-docs-utils/forge-doc-parser'); const { generateFacetDoc, generateModuleDoc } = require('./generate-docs-utils/templates/templates'); const { enhanceWithAI, shouldSkipEnhancement, addFallbackContent } = require('./generate-docs-utils/ai-enhancement'); +const { syncDocsStructure } = require('./generate-docs-utils/category-generator'); + +// ============================================================================ +// Tracking +// ============================================================================ -// Track processed files for summary +/** Track processed files for summary */ const processedFiles = { facets: [], modules: [], @@ -40,6 +52,10 @@ const processedFiles = { errors: [], }; +// ============================================================================ +// Processing Functions +// ============================================================================ + /** * Process a single forge doc markdown file * @param {string} forgeDocFile - Path to forge doc markdown file @@ -56,7 +72,7 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { // Parse the forge doc markdown const data = parseForgeDocMarkdown(content, forgeDocFile); - + // Add source file path for parameter extraction if (solFilePath) { data.sourceFilePath = solFilePath; @@ -68,7 +84,7 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { return false; } - // Skip interfaces - only generate docs for facets and modules + // Skip interfaces if (isInterface(data.title, content)) { console.log(`Skipping interface: ${data.title}`); processedFiles.skipped.push({ file: forgeDocFile, reason: 'Interface (filtered)' }); @@ -85,17 +101,18 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { } // Apply smart description fallback for facets with generic descriptions - // (Modules are handled in processAggregatedFiles) if (contractType === 'facet') { - // Check if description looks like an enum definition (e.g., "Add=0, Replace=1, Remove=2") - const looksLikeEnum = data.description && /\w+\s*=\s*\d+/.test(data.description) && + const looksLikeEnum = + data.description && + /\w+\s*=\s*\d+/.test(data.description) && (data.description.match(/\w+\s*=\s*\d+/g) || []).length >= 2; - - const isGenericDescription = !data.description || + + const isGenericDescription = + !data.description || data.description.startsWith('Contract documentation for') || looksLikeEnum || data.description.length < 20; - + if (isGenericDescription) { const generatedDescription = generateDescriptionFromName(data.title); if (generatedDescription) { @@ -106,10 +123,16 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { } } - // Check if we should skip AI enhancement (e.g., for interfaces or when SKIP_ENHANCEMENT is set) + // Get output path (mirrors src/ structure) + const pathInfo = getOutputPath(solFilePath, contractType); + + // Get smart sidebar position + data.position = getSidebarPosition(data.title); + + // Check if we should skip AI enhancement const skipAIEnhancement = shouldSkipEnhancement(data) || process.env.SKIP_ENHANCEMENT === 'true'; - // Enhance with AI if not skipped, otherwise add fallback content + // Enhance with AI if not skipped let enhancedData = data; if (!skipAIEnhancement) { const token = process.env.GITHUB_TOKEN; @@ -119,26 +142,26 @@ async function processForgeDocFile(forgeDocFile, solFilePath) { enhancedData = addFallbackContent(data, contractType); } - const mdxContent = contractType === 'module' - ? generateModuleDoc(enhancedData) - : generateFacetDoc(enhancedData); + // Generate MDX content + const mdxContent = contractType === 'module' ? generateModuleDoc(enhancedData) : generateFacetDoc(enhancedData); + + // Ensure output directory exists + fs.mkdirSync(pathInfo.outputDir, { recursive: true }); - const outputDir = getOutputDir(contractType); - const outputFile = path.join(outputDir, `${data.title}.mdx`); + // Write the file + if (writeFileSafe(pathInfo.outputFile, mdxContent)) { + console.log('✅ Generated:', pathInfo.outputFile); - if (writeFileSafe(outputFile, mdxContent)) { - console.log('✅ Generated:', outputFile); - if (contractType === 'module') { - processedFiles.modules.push({ title: data.title, file: outputFile }); + processedFiles.modules.push({ title: data.title, file: pathInfo.outputFile }); } else { - processedFiles.facets.push({ title: data.title, file: outputFile }); + processedFiles.facets.push({ title: data.title, file: pathInfo.outputFile }); } - + return true; } - processedFiles.errors.push({ file: outputFile, error: 'Could not write file' }); + processedFiles.errors.push({ file: pathInfo.outputFile, error: 'Could not write file' }); return false; } @@ -192,21 +215,21 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { } const data = aggregateParsedItems(parsedItems, solFilePath); - + data.sourceFilePath = solFilePath; if (!data.title) { data.title = extractModuleNameFromPath(solFilePath); } + // Try to get description from source file const sourceDescription = extractModuleDescriptionFromSource(solFilePath); if (sourceDescription) { data.description = sourceDescription; data.subtitle = sourceDescription; data.overview = sourceDescription; } else { - // Use smart description generator based on contract name - // This handles cases where source file has no file-level @title/@notice + // Use smart description generator const generatedDescription = generateDescriptionFromName(data.title); if (generatedDescription) { data.description = generatedDescription; @@ -215,7 +238,11 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { } else { // Last resort fallback const genericDescription = `Module providing internal functions for ${data.title}`; - if (!data.description || data.description.includes('Event emitted') || data.description.includes('Thrown when')) { + if ( + !data.description || + data.description.includes('Event emitted') || + data.description.includes('Thrown when') + ) { data.description = genericDescription; data.subtitle = genericDescription; data.overview = genericDescription; @@ -234,6 +261,12 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { data.storageInfo = extractStorageInfo(data); } + // Get output path (mirrors src/ structure) + const pathInfo = getOutputPath(solFilePath, contractType); + + // Get smart sidebar position + data.position = getSidebarPosition(data.title); + const skipAIEnhancement = shouldSkipEnhancement(data) || process.env.SKIP_ENHANCEMENT === 'true'; let enhancedData = data; @@ -242,33 +275,29 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { enhancedData = await enhanceWithAI(data, contractType, token); } else { console.log(`Skipping AI enhancement for ${data.title}`); - // Add fallback content when skipping AI enhancement enhancedData = addFallbackContent(data, contractType); } // Generate MDX content - const mdxContent = contractType === 'module' - ? generateModuleDoc(enhancedData) - : generateFacetDoc(enhancedData); + const mdxContent = contractType === 'module' ? generateModuleDoc(enhancedData) : generateFacetDoc(enhancedData); - // Determine output path - const outputDir = getOutputDir(contractType); - const outputFile = path.join(outputDir, `${data.title}.mdx`); + // Ensure output directory exists + fs.mkdirSync(pathInfo.outputDir, { recursive: true }); // Write the file - if (writeFileSafe(outputFile, mdxContent)) { - console.log('✅ Generated:', outputFile); - + if (writeFileSafe(pathInfo.outputFile, mdxContent)) { + console.log('✅ Generated:', pathInfo.outputFile); + if (contractType === 'module') { - processedFiles.modules.push({ title: data.title, file: outputFile }); + processedFiles.modules.push({ title: data.title, file: pathInfo.outputFile }); } else { - processedFiles.facets.push({ title: data.title, file: outputFile }); + processedFiles.facets.push({ title: data.title, file: pathInfo.outputFile }); } - + return true; } - processedFiles.errors.push({ file: outputFile, error: 'Could not write file' }); + processedFiles.errors.push({ file: pathInfo.outputFile, error: 'Could not write file' }); return false; } @@ -298,6 +327,10 @@ async function processSolFile(solFilePath) { } } +// ============================================================================ +// Summary & Reporting +// ============================================================================ + /** * Print processing summary */ @@ -351,12 +384,27 @@ function writeSummaryFile() { writeFileSafe('docgen-summary.json', JSON.stringify(summary, null, 2)); } +// ============================================================================ +// Main Entry Point +// ============================================================================ + /** * Main entry point */ async function main() { console.log('Compose Documentation Generator\n'); + // Step 1: Sync docs structure with src structure + console.log('📁 Syncing documentation structure with source...'); + const syncResult = syncDocsStructure(); + + if (syncResult.created.length > 0) { + console.log(` Created ${syncResult.created.length} new categories:`); + syncResult.created.forEach((c) => console.log(` ✅ ${c}`)); + } + console.log(` Total categories: ${syncResult.total}\n`); + + // Step 2: Determine which files to process const args = process.argv.slice(2); let solFiles = []; @@ -367,7 +415,7 @@ async function main() { const changedFilesPath = args[0]; console.log(`Reading changed files from: ${changedFilesPath}`); solFiles = readChangedFilesFromFile(changedFilesPath); - + if (solFiles.length === 0) { console.log('No files in list, checking git diff...'); const { getChangedSolFiles } = require('./generate-docs-utils/doc-generation-utils'); @@ -386,11 +434,13 @@ async function main() { console.log(`Found ${solFiles.length} Solidity file(s) to process\n`); + // Step 3: Process each file for (const solFile of solFiles) { await processSolFile(solFile); console.log(''); } + // Step 4: Print summary printSummary(); writeSummaryFile(); } @@ -399,5 +449,3 @@ main().catch((error) => { console.error(`Fatal error: ${error}`); process.exit(1); }); - - diff --git a/.github/scripts/sync-docs-structure.js b/.github/scripts/sync-docs-structure.js new file mode 100644 index 00000000..d2ff640b --- /dev/null +++ b/.github/scripts/sync-docs-structure.js @@ -0,0 +1,210 @@ +#!/usr/bin/env node +/** + * Sync Documentation Structure + * + * Standalone script to mirror the src/ folder structure in website/docs/contracts/ + * Creates _category_.json files for Docusaurus navigation. + * + * Usage: + * node .github/scripts/sync-docs-structure.js [options] + * + * Options: + * --dry-run Show what would be created without making changes + * --verbose Show detailed output + * --help Show this help message + * + * Examples: + * node .github/scripts/sync-docs-structure.js + * node .github/scripts/sync-docs-structure.js --dry-run + */ + +const fs = require('fs'); +const path = require('path'); + +// Handle running from different directories +const scriptDir = __dirname; +process.chdir(path.join(scriptDir, '../..')); + +const { syncDocsStructure, scanSourceStructure } = require('./generate-docs-utils/category-generator'); + +// ============================================================================ +// CLI Parsing +// ============================================================================ + +const args = process.argv.slice(2); +const options = { + dryRun: args.includes('--dry-run'), + verbose: args.includes('--verbose'), + help: args.includes('--help') || args.includes('-h'), +}; + +// ============================================================================ +// Help +// ============================================================================ + +function showHelp() { + console.log(` +Sync Documentation Structure + +Mirrors the src/ folder structure in website/docs/contracts/ +Creates _category_.json files for Docusaurus navigation. + +Usage: + node .github/scripts/sync-docs-structure.js [options] + +Options: + --dry-run Show what would be created without making changes + --verbose Show detailed output + --help, -h Show this help message + +Examples: + node .github/scripts/sync-docs-structure.js + node .github/scripts/sync-docs-structure.js --dry-run +`); +} + +// ============================================================================ +// Tree Display +// ============================================================================ + +/** + * Display the source structure as a tree + * @param {Map} structure - Structure map from scanSourceStructure + */ +function displayTree(structure) { + console.log('\n📂 Source Structure (src/)\n'); + + // Sort by path for consistent display + const sorted = Array.from(structure.entries()).sort((a, b) => a[0].localeCompare(b[0])); + + // Build tree visualization + const tree = new Map(); + for (const [pathStr] of sorted) { + const parts = pathStr.split('/'); + let current = tree; + for (const part of parts) { + if (!current.has(part)) { + current.set(part, new Map()); + } + current = current.get(part); + } + } + + // Print tree + function printTree(node, prefix = '', isLast = true) { + const entries = Array.from(node.entries()); + entries.forEach(([name, children], index) => { + const isLastItem = index === entries.length - 1; + const connector = isLastItem ? '└── ' : '├── '; + const icon = children.size > 0 ? '📁' : '📄'; + console.log(`${prefix}${connector}${icon} ${name}`); + + if (children.size > 0) { + const newPrefix = prefix + (isLastItem ? ' ' : '│ '); + printTree(children, newPrefix, isLastItem); + } + }); + } + + printTree(tree); + console.log(''); +} + +// ============================================================================ +// Dry Run Mode +// ============================================================================ + +/** + * Simulate sync without making changes + * @param {Map} structure - Structure map + */ +function dryRun(structure) { + console.log('\n🔍 Dry Run Mode - No changes will be made\n'); + + const contractsDir = 'website/docs/contracts'; + let wouldCreate = 0; + let alreadyExists = 0; + + // Check base category + const baseCategoryFile = path.join(contractsDir, '_category_.json'); + if (fs.existsSync(baseCategoryFile)) { + console.log(` ✓ ${baseCategoryFile} (exists)`); + alreadyExists++; + } else { + console.log(` + ${baseCategoryFile} (would create)`); + wouldCreate++; + } + + // Check each category + for (const [relativePath] of structure) { + const categoryFile = path.join(contractsDir, relativePath, '_category_.json'); + if (fs.existsSync(categoryFile)) { + if (options.verbose) { + console.log(` ✓ ${categoryFile} (exists)`); + } + alreadyExists++; + } else { + console.log(` + ${categoryFile} (would create)`); + wouldCreate++; + } + } + + console.log(`\nSummary:`); + console.log(` Would create: ${wouldCreate} category files`); + console.log(` Already exist: ${alreadyExists} category files`); + console.log(`\nRun without --dry-run to apply changes.\n`); +} + +// ============================================================================ +// Main +// ============================================================================ + +function main() { + if (options.help) { + showHelp(); + return; + } + + console.log('📚 Sync Documentation Structure\n'); + console.log('Scanning src/ directory...'); + + const structure = scanSourceStructure(); + console.log(`Found ${structure.size} directories with Solidity files`); + + if (options.verbose || structure.size <= 20) { + displayTree(structure); + } + + if (options.dryRun) { + dryRun(structure); + return; + } + + console.log('Creating documentation structure...\n'); + const result = syncDocsStructure(); + + // Display results + console.log('='.repeat(50)); + console.log('Summary'); + console.log('='.repeat(50)); + console.log(`Created: ${result.created.length} categories`); + console.log(`Existing: ${result.existing.length} categories`); + console.log(`Total: ${result.total} categories`); + + if (result.created.length > 0) { + console.log('\nNewly created:'); + result.created.forEach((c) => console.log(` ✅ ${c}`)); + } + + console.log('\n✨ Done!\n'); + + // Show next steps + console.log('Next steps:'); + console.log(' 1. Run documentation generator to populate content:'); + console.log(' node .github/scripts/generate-docs.js --all\n'); + console.log(' 2. Or generate docs for specific files:'); + console.log(' node .github/scripts/generate-docs.js path/to/changed-files.txt\n'); +} + +main(); + diff --git a/website/docs/contracts/access/AccessControl/_category_.json b/website/docs/contracts/access/AccessControl/_category_.json new file mode 100644 index 00000000..25db9246 --- /dev/null +++ b/website/docs/contracts/access/AccessControl/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Role-based access control (RBAC) pattern." + } +} diff --git a/website/docs/contracts/access/AccessControlPausable/_category_.json b/website/docs/contracts/access/AccessControlPausable/_category_.json new file mode 100644 index 00000000..ab207a3c --- /dev/null +++ b/website/docs/contracts/access/AccessControlPausable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Pausable Access Control", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "RBAC with pause functionality." + } +} diff --git a/website/docs/contracts/access/AccessControlTemporal/_category_.json b/website/docs/contracts/access/AccessControlTemporal/_category_.json new file mode 100644 index 00000000..72012bb3 --- /dev/null +++ b/website/docs/contracts/access/AccessControlTemporal/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Temporal Access Control", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Time-limited role-based access control." + } +} diff --git a/website/docs/contracts/access/Owner/_category_.json b/website/docs/contracts/access/Owner/_category_.json new file mode 100644 index 00000000..274b507b --- /dev/null +++ b/website/docs/contracts/access/Owner/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Owner", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Single-owner access control pattern." + } +} diff --git a/website/docs/contracts/access/OwnerTwoSteps/_category_.json b/website/docs/contracts/access/OwnerTwoSteps/_category_.json new file mode 100644 index 00000000..52fea0d0 --- /dev/null +++ b/website/docs/contracts/access/OwnerTwoSteps/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Two-Step Owner", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Two-step ownership transfer pattern." + } +} diff --git a/website/docs/contracts/access/_category_.json b/website/docs/contracts/access/_category_.json new file mode 100644 index 00000000..da73185e --- /dev/null +++ b/website/docs/contracts/access/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 2, + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "description": "Access control patterns for permission management in Compose diamonds." + } +} diff --git a/website/docs/contracts/diamond/_category_.json b/website/docs/contracts/diamond/_category_.json new file mode 100644 index 00000000..69336647 --- /dev/null +++ b/website/docs/contracts/diamond/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Diamond Core", + "position": 1, + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "description": "Core diamond proxy functionality for ERC-2535 diamonds." + } +} diff --git a/website/docs/contracts/diamond/example/_category_.json b/website/docs/contracts/diamond/example/_category_.json new file mode 100644 index 00000000..d94c8663 --- /dev/null +++ b/website/docs/contracts/diamond/example/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "example", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "example components for Compose diamonds." + } +} diff --git a/website/docs/contracts/facets/AccessControlFacet.mdx b/website/docs/contracts/facets/AccessControlFacet.mdx deleted file mode 100644 index bf82719a..00000000 --- a/website/docs/contracts/facets/AccessControlFacet.mdx +++ /dev/null @@ -1,547 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Role-based access control (RBAC) facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control (RBAC) facet for Compose diamonds - - - -- Supports standard RBAC patterns with predefined roles like `DEFAULT_ADMIN_ROLE`. -- Provides granular control over granting and revoking roles for individual accounts and in batches. -- Includes a `requireRole` function for easy inline access control checks within other facets. - - -## Overview - -The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling diamond administrators to grant, revoke, and manage roles for various accounts, ensuring secure and controlled access to diamond functionalities. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondProxy, DiamondInit} from "@compose/diamond-proxy/DiamondProxy.sol"; -import {AccessControlFacet} from "@compose/access-control/AccessControlFacet.sol"; - -contract MyDiamondInit is DiamondInit { - function init() public { - // ... other initializations ... - AccessControlFacet accessControlFacet = AccessControlFacet(address(this)); - bytes32 adminRole = accessControlFacet.getRoleAdmin(keccak256("DEFAULT_ADMIN_ROLE")); - accessControlFacet.grantRole(adminRole, msg.sender); - } -} - -contract MyDiamond is DiamondProxy { - function upgradeTo(address newImplementation) public { - // ... upgrade logic ... - } - - function execute() public { - AccessControlFacet accessControlFacet = AccessControlFacet(address(this)); - bytes32 MY_ROLE = keccak256("MY_ROLE"); - accessControlFacet.grantRole(MY_ROLE, tx.origin); - accessControlFacet.requireRole(MY_ROLE, tx.origin); - // ... execute protected function ... - } -}`} - - -## Best Practices - - -- Initialize roles and grant initial administrative privileges during diamond deployment using `DiamondInit`. -- Use `getRoleAdmin` and `setRoleAdmin` to manage role hierarchies and administrative delegation effectively. -- Leverage batch functions (`grantRoleBatch`, `revokeRoleBatch`) for efficient management of multiple accounts for a single role. - - -## Security Considerations - - -Ensure that the caller is authorized to manage roles by checking the role admin before calling `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch`. The `renounceRole` function should be used with caution as it permanently removes the caller's role. Input validation is critical; ensure that roles and account addresses are correctly formatted before being passed to functions. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlPausableFacet.mdx b/website/docs/contracts/facets/AccessControlPausableFacet.mdx deleted file mode 100644 index e5155bf6..00000000 --- a/website/docs/contracts/facets/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,375 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Role-based access control with pause functionality for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control with pause functionality for Compose diamonds - - - -- Granular role pausing: Allows selective disabling of functionalities tied to specific roles. -- Admin-controlled pausing: Only designated role administrators can pause or unpause roles. -- Integrated with role-based access control: Seamlessly combines pausing with existing role checks. - - -## Overview - -The AccessControlPausableFacet enhances a Compose diamond by integrating role-based access control with the ability to pause specific roles. This allows for granular control over functionality, enabling administrators to temporarily halt operations for certain roles without affecting the entire diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose/diamond-loupe/DiamondLoupeFacet.sol"; -import {DiamondCutFacet} from "@compose/diamond-cut/DiamondCutFacet.sol"; -import {AccessControlPausableFacet} from "@compose/access-control/AccessControlPausableFacet.sol"; - -contract DeployDiamond { - // ... deployment setup ... - - function deployDiamond() public { - // ... other facet deployments ... - - AccessControlPausableFacet accessControlPausableFacet = new AccessControlPausableFacet(); - - // Add AccessControlPausableFacet to the diamond - // ... Diamond Cut operations ... - - // Example: Get storage pointers - AccessControlPausableFacet.AccessControlPausableStorage storage acpStorage = AccessControlPausableFacet.getStorage(); - // AccessControlPausableFacet.AccessControlStorage storage acStorage = AccessControlPausableFacet.getAccessControlStorage(); - - // Example: Pause a role (assuming 'MY_ROLE' is defined and caller is admin) - // accessControlPausableFacet.pauseRole(keccak256("MY_ROLE")); - - // Example: Check if a role is paused - // bool isPaused = accessControlPausableFacet.isRolePaused(keccak256("MY_ROLE")); - } -}`} - - -## Best Practices - - -- Initialize roles and assign administrators via an upgrade function or during deployment to ensure proper access control setup. -- Use `requireRoleNotPaused` within your facet functions to enforce the paused state of roles before executing sensitive operations. -- Store role identifiers consistently (e.g., using `keccak256` hashes of role names) to avoid discrepancies when checking pause status or granting permissions. - - -## Security Considerations - - -Ensure that the caller attempting to pause or unpause a role possesses the necessary administrative privileges for that specific role. Reentrancy is not a direct concern for `pauseRole` and `unpauseRole` as they only modify internal state and do not make external calls. However, downstream functions that check the paused state must be audited for reentrancy risks. - - -
- -
- - diff --git a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx b/website/docs/contracts/facets/AccessControlTemporalFacet.mdx deleted file mode 100644 index e70c115e..00000000 --- a/website/docs/contracts/facets/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,467 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Time-limited role-based access control for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Time-limited role-based access control for Compose diamonds - - - -- Time-limited role assignments with automatic expiry. -- Granular control over temporary access permissions. -- Integration with Compose's standard role-based access control system. - - -## Overview - -The AccessControlTemporalFacet extends Compose's access control with time-limited role assignments. It allows roles to be granted for a specific duration, automatically expiring after the set timestamp. This facet is crucial for managing temporary permissions within a diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose/diamond-contracts/facets/DiamondLoupeFacet.sol"; -import {AccessControlFacet} from "@compose/diamond-contracts/facets/AccessControlFacet.sol"; -import {AccessControlTemporalFacet} from "@compose/diamond-contracts/facets/AccessControlTemporalFacet.sol"; - -contract DeployDiamondExample { - // Assume diamond and facets are already deployed and selectors registered - // For example purposes, we'll interact with the facets directly. - address diamondAddress; // Address of your deployed diamond - - function grantTempRole() public { - AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(diamondAddress); - address user = address(1); // Example user address - bytes32 role = AccessControlFacet.DEFAULT_ADMIN_ROLE(); // Example role - uint256 expiryTimestamp = block.timestamp + 3600; // Role expires in 1 hour - - // Caller must be the admin of the role to grant it with expiry - temporalFacet.grantRoleWithExpiry(role, user, expiryTimestamp); - } - - function checkRole() public view returns (bool) { - AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(diamondAddress); - address user = address(1); // Example user address - bytes32 role = AccessControlFacet.DEFAULT_ADMIN_ROLE(); // Example role - - // This will revert if the role is expired or not held - try temporalFacet.requireValidRole(role, user) { - return true; - } catch (bytes memory) { - return false; - } - } - - function revokeTempRole() public { - AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(diamondAddress); - address user = address(1); // Example user address - bytes32 role = AccessControlFacet.DEFAULT_ADMIN_ROLE(); // Example role - - // Caller must be the admin of the role to revoke it - temporalFacet.revokeTemporalRole(role, user); - } -}`} - - -## Best Practices - - -- Initialize the `AccessControlFacet` before using `AccessControlTemporalFacet` to set up roles and administrators. -- Ensure that the caller attempting to grant or revoke temporal roles is the designated admin for that specific role. -- Regularly monitor role expiry to ensure timely revocation of temporary permissions, especially for sensitive operations. - - -## Security Considerations - - -The `grantRoleWithExpiry` and `revokeTemporalRole` functions are restricted to the admin of the role, preventing unauthorized role manipulation. The `requireValidRole` function includes checks for both role existence and expiry, reverting if the role is invalid or expired, mitigating risks associated with stale permissions. Reentrancy is not a concern as these functions do not make external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/DiamondCutFacet.mdx b/website/docs/contracts/facets/DiamondCutFacet.mdx deleted file mode 100644 index fe0f31af..00000000 --- a/website/docs/contracts/facets/DiamondCutFacet.mdx +++ /dev/null @@ -1,371 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Diamond upgrade (cut) facet for ERC-2535 diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond upgrade (cut) facet for ERC-2535 diamonds - - - -- Self-contained facet with no imports or inheritance -- Only `external` and `internal` function visibility -- Follows Compose readability-first conventions -- Ready for diamond integration - - -## Overview - -Diamond upgrade (cut) facet for ERC-2535 diamonds - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -
- -
- - diff --git a/website/docs/contracts/facets/DiamondLoupeFacet.mdx b/website/docs/contracts/facets/DiamondLoupeFacet.mdx deleted file mode 100644 index 7825e4c6..00000000 --- a/website/docs/contracts/facets/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,255 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "The functions in DiamondLoupeFacet MUST be added to a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -The functions in DiamondLoupeFacet MUST be added to a diamond. - - - -- Provides comprehensive introspection of diamond facets and their associated function selectors. -- Offers efficient querying of facet addresses based on function selectors. -- Optimized for performance on diamonds with numerous facets and selectors, utilizing memory-efficient hashing techniques. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond proxy. It allows developers to query which facets are registered, the selectors they support, and the addresses of these facets. This facet is crucial for understanding the diamond's internal structure and for building external tooling or logic that interacts with specific diamond functionalities. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose-protocol/diamond/contracts/facets/DiamondLoupeFacet.sol"; -import {IDiamondLoupe} from "@compose-protocol/diamond/contracts/interfaces/IDiamondLoupe.sol"; - -contract DiamondLoupeConsumer { - IDiamondLoupe public diamondLoupe; - - constructor(address _diamondAddress) { - diamondLoupe = IDiamondLoupe(_diamondAddress); - } - - function getAllFacets() public view returns (IDiamondLoupe.Facet[] memory) { - return diamondLoupe.facets(); - } - - function getFacetAddr(bytes4 _selector) public view returns (address) { - return diamondLoupe.facetAddress(_selector); - } - - function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { - return diamondLoupe.facetFunctionSelectors(_facet); - } -}`} - - -## Best Practices - - -- Initialize the `DiamondLoupeFacet` during diamond deployment to ensure introspection functions are available from the start. -- Access `DiamondLoupeFacet` functions via the diamond proxy address to ensure all calls are routed correctly and adhere to diamond proxy logic. -- When querying for facet information, be mindful of gas costs, especially with `facets()` on diamonds with a very large number of facets and selectors; consider using more specific functions like `facetAddress()` or `facetFunctionSelectors()` when possible. - - -## Security Considerations - - -The `DiamondLoupeFacet` is generally read-only and does not modify state, thus posing minimal direct security risks. However, the information it provides can be critical for security audits and for understanding access control mechanisms implemented by other facets. Ensure that the diamond's upgradeability is managed securely, as changes to facet registration or function selector mappings could impact the integrity of the information returned by this facet. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC1155Facet.mdx b/website/docs/contracts/facets/ERC1155Facet.mdx deleted file mode 100644 index 4c2b71e6..00000000 --- a/website/docs/contracts/facets/ERC1155Facet.mdx +++ /dev/null @@ -1,699 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "ERC-1155 multi-token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-1155 multi-token facet for Compose diamonds - - - -- Implements the ERC-1155 Multi-Token Standard, supporting both fungible and non-fungible tokens. -- Provides batched transfer and balance checking functions (`balanceOfBatch`, `safeBatchTransferFrom`) for efficiency. -- Supports customizable token URIs, allowing for dynamic metadata based on token ID and an optional base URI. - - -## Overview - -The ERC1155Facet implements the ERC-1155 multi-token standard for Compose diamonds. It provides core functionalities for managing and transferring fungible and non-fungible tokens within the diamond's composable architecture. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut, IERC1155Facet} from \"@compose/contracts/src/interfaces/IDiamond.sol\"; - -contract ERC1155Deployer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function deployERC1155Facet(address _diamondCutAddress) external { - // Assume ERC1155Facet contract is deployed and its address is known - address erc1155FacetAddress = address(0xYourERC1155FacetAddress); - - // Define the functions to be added by this facet - bytes4[] memory selectors = new bytes4[](8); - selectors[0] = IERC1155Facet.getStorage.selector; - selectors[1] = IERC1155Facet.uri.selector; - selectors[2] = IERC1155Facet.balanceOf.selector; - selectors[3] = IERC1155Facet.balanceOfBatch.selector; - selectors[4] = IERC1155Facet.setApprovalForAll.selector; - selectors[5] = IERC1155Facet.isApprovedForAll.selector; - selectors[6] = IERC1155Facet.safeTransferFrom.selector; - selectors[7] = IERC1155Facet.safeBatchTransferFrom.selector; - - // Prepare the DiamondCut data - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: erc1155FacetAddress, - action: IDiamondCut.FacetCutAction.ADD, - functionSelectors: selectors - }); - - // Execute the diamond cut to add the ERC1155 facet - bytes memory data = abi.encodeWithSelector(IDiamondCut.diamondCut.selector, cuts); - // Assuming IDiamondCut interface is accessible and the diamond supports the cut - // The actual call would be to the diamond proxy's fallback or delegatecall mechanism - // For simplicity, this example shows the intended data structure. - // In a real deployment, you'd call the diamond proxy directly with this data. - // Example: _diamondCutAddress.diamondCut(cuts); - } - - // Example of calling functions on the ERC1155 facet via the diamond proxy - function callERC1155Functions() external { - // Assuming IERC1155Facet interface is available and diamondAddress is set - IERC1155Facet erc1155 = IERC1155Facet(diamondAddress); - - // Example: Get balance of token ID 1 for address(this) - uint256 balance = erc1155.balanceOf(address(this), 1); - - // Example: Get URI for token ID 2 - string memory tokenUri = erc1155.uri(2); - } -}`} - - -## Best Practices - - -- Initialize the ERC1155 facet with necessary configuration, such as setting a base URI if token-specific URIs will be used for concatenation. Ensure the diamond's storage slot for ERC1155 is correctly set during deployment or upgrades. -- When upgrading, ensure that the function selectors for the ERC1155 facet are correctly managed to maintain compatibility and prevent accidental removal of critical functions. -- Carefully manage access control for functions that modify state (e.g., `setApprovalForAll`) by integrating with the diamond's access control mechanisms, if applicable, to restrict sensitive operations. - - -## Security Considerations - - -The `safeTransferFrom` and `safeBatchTransferFrom` functions must be called with valid parameters to prevent unintended token transfers or loss. Ensure that the caller has sufficient allowance or ownership of the tokens being transferred. The `setApprovalForAll` function grants broad permissions to an operator, so callers should exercise caution when approving addresses. Reentrancy is not an explicit concern within the facet's core transfer logic as it relies on standard ERC-1155 patterns, but external contract interactions initiated by the diamond proxy should be audited for reentrancy risks. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx b/website/docs/contracts/facets/ERC20BridgeableFacet.mdx deleted file mode 100644 index 0dd55c0c..00000000 --- a/website/docs/contracts/facets/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,434 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "ERC-20 token bridgeable facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token bridgeable facet for Compose diamonds - - - -- Enables secure cross-chain minting and burning of ERC-20 tokens. -- Enforces `trusted-bridge` role for all cross-chain mint/burn operations. -- Provides internal helper functions to access facet storage safely. - - -## Overview - -The ERC20BridgeableFacet enables cross-chain interoperability for ERC-20 tokens within a Compose diamond. It provides functions for minting and burning tokens on behalf of trusted bridges, ensuring secure and controlled cross-chain token transfers. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableFacet} from "../facets/ERC20BridgeableFacet.sol"; -import {IDiamondCut} from "@compose/diamond-proxy/contracts/interfaces/IDiamondCut.sol"; - -contract DeployERC20BridgeableFacet { - address diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function deploy() public { - // Assume ERC20BridgeableFacet is already compiled and its bytecode is available - bytes memory erc20BridgeableFacetBytecode = type(ERC20BridgeableFacet).creation.runtime.bytecode; - - IDiamondCut(diamondAddress).diamondCut( - IDiamondCut.FacetCut[] memory setAction, - address(0), // clear previous diamond admin - bytes('') // init data - ); - - // Add the ERC20BridgeableFacet - IDiamondCut.FacetCut[] memory facetCuts = new IDiamondCut.FacetCut[](1); - facetCuts[0] = IDiamondCut.FacetCut( - ERC20BridgeableFacet(address(0)).crosschainMint.selector, - ERC20BridgeableFacet(address(0)).crosschainMint.selector, - erc20BridgeableFacetBytecode - ); - - IDiamondCut(diamondAddress).diamondCut( - facetCuts, - address(0), // clear previous diamond admin - bytes('') // init data - ); - } - - function exampleCrosschainMint(address _token, address _to, uint256 _amount) public { - IERC20BridgeableFacet(diamondAddress).crosschainMint(_token, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is correctly assigned in the AccessControl facet for authorized cross-chain operations. -- Initialize the diamond with this facet by including it in the initial `diamondCut` or a subsequent upgrade transaction. -- Use `getERC20Storage` and `getAccessControlStorage` to safely access facet-specific storage within your custom facets or logic. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role. Ensure this role is granted only to verified and secure bridge operators. The `checkTokenBridge` internal function prevents unauthorized calls by verifying the caller's role and ensuring the caller is not the zero address. Reentrancy is not a direct concern for mint/burn operations, but ensure any custom logic interacting with this facet is reentrancy-safe. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20BurnFacet.mdx b/website/docs/contracts/facets/ERC20BurnFacet.mdx deleted file mode 100644 index ed741d6a..00000000 --- a/website/docs/contracts/facets/ERC20BurnFacet.mdx +++ /dev/null @@ -1,252 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "ERC-20 token burn facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token burn facet for Compose diamonds - - - -- Supports burning tokens directly from a user's balance. -- Enables burning tokens from another account using pre-approved allowances. -- Emits `Transfer` events to the zero address, adhering to ERC-20 burn event standards. - - -## Overview - -The ERC20BurnFacet enables the destruction of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account via allowance, facilitating token supply management and deflationary mechanics. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BurnFacet} from "@compose/diamond/facets/ERC20/ERC20BurnFacet.sol"; - -contract ERC20BurnConsumer { - address immutable _diamondAddress; - - constructor(address diamondAddress) { - _diamondAddress = diamondAddress; - } - - function consumeBurn() external { - IERC20BurnFacet burnFacet = IERC20BurnFacet(_diamondAddress); - // Example: Burn 100 tokens from the caller's balance - burnFacet.burn(100); - } - - function consumeBurnFrom(address _from, uint256 _amount) external { - IERC20BurnFacet burnFacet = IERC20BurnFacet(_diamondAddress); - // Example: Burn 50 tokens from _from's balance, assuming caller has allowance - burnFacet.burnFrom(_from, _amount); - } -}`} - - -## Best Practices - - -- Initialize the diamond with this facet to enable burning capabilities. -- Ensure appropriate access control is set for `burnFrom` if restricted burning is desired. -- Understand that `burn` and `burnFrom` reduce the total token supply. - - -## Security Considerations - - -The `burn` function is permissionless and operates on the caller's balance. The `burnFrom` function requires the caller to have an allowance set for the tokens being burned. No reentrancy concerns exist as these functions do not make external calls. Input validation ensures non-zero amounts are burned. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20Facet.mdx b/website/docs/contracts/facets/ERC20Facet.mdx deleted file mode 100644 index 13b34f27..00000000 --- a/website/docs/contracts/facets/ERC20Facet.mdx +++ /dev/null @@ -1,578 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "ERC-20 fungible token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 fungible token facet for Compose diamonds - - - -- Implements the full ERC-20 standard interface for fungible tokens. -- Integrates seamlessly into the Compose diamond architecture, allowing composability with other facets. -- Provides direct access to token state variables via `getStorage` for off-chain or internal tooling. - - -## Overview - -The ERC20Facet provides standard ERC-20 fungible token functionality within a Compose diamond. It manages token metadata, supply, balances, and allowances, enabling seamless integration with other diamond facets and external applications. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; -import {ERC20Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20Facet.sol"; - -contract DeployERC20Diamond { - // Assume DiamondInit and DiamondLoupeFacet are deployed and initialized - address internal diamondProxyAddress; - - function deploy() public { - // ... deployment logic for DiamondInit and DiamondLoupeFacet ... - - // Deploy ERC20Facet - ERC20Facet erc20Facet = new ERC20Facet(); - - // Prepare facet cut for ERC20Facet - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: address(erc20Facet), - action: IDiamondCut.FacetCutAction.ADD, - selectors: ERC20Facet.getSelectors() - }); - - // Add ERC20Facet to the diamond - // Assumes diamondProxyAddress is already set and initialized - // diamond.diamondCut(cut, address(0), ""); // Replace with actual diamond instance and method - - // Interact with the ERC20 token - // Assumes diamondProxyAddress is the address of your ERC20 token diamond - // ERC20Facet erc20Token = ERC20Facet(diamondProxyAddress); - // uint256 tokenSupply = erc20Token.totalSupply(); - // address tokenOwner = msg.sender; - // erc20Token.transfer(tokenOwner, 100 * 10**18); - } -}`} - - -## Best Practices - - -- Initialize ERC20 token metadata (name, symbol, decimals) via the DiamondInit facet during diamond deployment. -- Leverage the diamond's access control mechanisms for functions like `approve` and `transferFrom` if restricted ownership or permissions are required. -- When upgrading the ERC20Facet, ensure the storage layout remains compatible or use versioned storage patterns to avoid data corruption. - - -## Security Considerations - - -Ensure proper access control is implemented in the diamond's upgrade mechanism to prevent unauthorized facet replacements. Input validation for `transfer` and `transferFrom` functions (e.g., checking for zero addresses and sufficient balances) is crucial. The `approve` function can be susceptible to front-running if not handled carefully in conjunction with diamond-level transaction ordering or meta-transaction relays. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC20PermitFacet.mdx b/website/docs/contracts/facets/ERC20PermitFacet.mdx deleted file mode 100644 index 20b374e1..00000000 --- a/website/docs/contracts/facets/ERC20PermitFacet.mdx +++ /dev/null @@ -1,334 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "ERC-20 token permit facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token permit facet for Compose diamonds - - - -- Implements EIP-2612 for gasless token approvals via signatures. -- Provides `nonces` and `DOMAIN_SEPARATOR` for signature generation and validation. -- Allows setting token allowances without requiring a separate `approve` transaction. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant token transfers within a Compose diamond. It provides the necessary functions to manage nonces, retrieve the domain separator, and execute token allowances via signed messages, enhancing user experience by removing the need for separate approval transactions. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20PermitFacet} from "@compose/facets/ERC20PermitFacet.sol"; -import {DiamondProxy, DiamondInit} from "@compose/core/"; - -contract MyDiamondInit is DiamondInit { - function init() public override { - // ... other initializations ... - ERC20PermitFacet.addFacet(msg.sender); - } -} - -contract MyDiamond is DiamondProxy { - using ERC20PermitFacet for ERC20PermitFacet; - - function getERC20PermitFacet() public pure returns (ERC20PermitFacet) { - return ERC20PermitFacet(address(this)); - } - - // Example of calling permit - function permitToken(address owner, address spender, uint256 value, uint256 deadline, bytes calldata signature) external { - // Assuming the ERC20 token contract is accessible via the diamond - // You would typically have an ERC20Facet or similar to interact with the token - // For demonstration, we call permit directly on the facet instance - getERC20PermitFacet().permit(owner, spender, value, deadline, signature); - } -}`} - - -## Best Practices - - -- Initialize the `ERC20PermitFacet` during diamond deployment using an `IDiamondInit` contract. -- Ensure the underlying ERC-20 token contract is correctly configured and accessible through the diamond's routing. -- Implement robust signature verification logic in off-chain or consumer contracts before submitting permit calls to the diamond. - - -## Security Considerations - - -The `permit` function is susceptible to replay attacks if the `DOMAIN_SEPARATOR` is not properly constructed or if signatures are reused across different chains or diamond instances. Ensure the `deadline` parameter is used to limit the validity of permits. The `nonces` must be managed correctly to prevent signature reuse. Access control for calling `permit` should be considered based on the diamond's overall authorization strategy. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC6909Facet.mdx b/website/docs/contracts/facets/ERC6909Facet.mdx deleted file mode 100644 index 5f3cf847..00000000 --- a/website/docs/contracts/facets/ERC6909Facet.mdx +++ /dev/null @@ -1,530 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "ERC-6909 minimal multi-token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-6909 minimal multi-token facet for Compose diamonds - - - -- Implements the ERC-6909 minimal multi-token standard. -- Supports individual token balances and allowances per token ID. -- Enables operator approvals for delegated token management. - - -## Overview - -The ERC6909Facet implements the minimal multi-token standard for Compose diamonds. It provides essential functions for managing token balances, allowances, and operator approvals, enabling flexible token interactions within the diamond proxy architecture. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909, ERC6909Facet} from "@compose/contracts/facets/ERC6909/ERC6909Facet.sol"; - -// Assume DiamondInit and DiamondCut are deployed and configured - -contract ERC6909User { - address internal diamondProxy; - - function initialize(address _diamondProxy) public { - diamondProxy = _diamondProxy; - } - - function getUserBalance(address _user, uint256 _id) public view returns (uint256) { - // Call the ERC6909Facet's balanceOf function via the diamond proxy - return ERC6909Facet(diamondProxy).balanceOf(_user, _id); - } - - function transferTokens(address _to, uint256 _id, uint256 _amount) public { - // Call the ERC6909Facet's transfer function via the diamond proxy - ERC6909Facet(diamondProxy).transfer(_to, _id, _amount); - } - - function approveSpender(address _spender, uint256 _id, uint256 _amount) public { - // Call the ERC6909Facet's approve function via the diamond proxy - ERC6909Facet(diamondProxy).approve(_spender, _id, _amount); - } -}`} - - -## Best Practices - - -- Initialize the ERC6909Facet with the correct storage slot during diamond deployment. -- Use the `approve` function to grant allowances before calling `transferFrom`. -- Leverage `setOperator` for managing trusted third-party token management. - - -## Security Considerations - - -Ensure proper access control is implemented at the diamond proxy level for sensitive functions like `transferFrom` and `approve` if they are restricted. Review operator approvals regularly to prevent unauthorized token movements. Input validation for token IDs, amounts, and addresses should be handled carefully to prevent unexpected behavior. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721BurnFacet.mdx b/website/docs/contracts/facets/ERC721BurnFacet.mdx deleted file mode 100644 index 249d1720..00000000 --- a/website/docs/contracts/facets/ERC721BurnFacet.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "ERC-721 NFT burn facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 NFT burn facet for Compose diamonds - - - -- Enables the destruction of ERC-721 tokens, permanently removing them from the contract's state. -- Integrates seamlessly with the Compose diamond pattern for modular NFT functionality. -- Adheres to ERC-721 token burning semantics, including removal from enumeration tracking. - - -## Overview - -The ERC721BurnFacet provides the functionality to burn (destroy) ERC-721 non-fungible tokens within a Compose diamond. It allows for the permanent removal of tokens from circulation and enumeration tracking, adhering to ERC-721 standards. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "@compose/facets/erc721/ERC721BurnFacet.sol"; -import {DiamondProxy} from "@compose/core/DiamondProxy.sol"; - -contract ERC721BurnConsumer is DiamondProxy { - IERC721BurnFacet public erc721BurnFacet; - - function setErc721BurnFacet(address _facetAddress) external onlyOwner { - erc721BurnFacet = IERC721BurnFacet(_facetAddress); - } - - function burnToken(uint256 _tokenId) external { - // Ensure the caller has ownership or is approved - // ... access control logic here ... - erc721BurnFacet.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Initialize the `ERC721BurnFacet` via the diamond upgrade mechanism, ensuring it is correctly registered and accessible. -- Implement robust access control within your diamond's logic layer (e.g., an access control facet) to determine who can call the `burn` function for a given token. -- Ensure the underlying ERC721 storage layout is compatible with the `STORAGE_POSITION` defined within the `ERC721BurnFacet`. - - -## Security Considerations - - -The `burn` function should only be callable by the token owner or an address approved on their behalf. Implement appropriate access control checks before invoking `ERC721BurnFacet.burn`. The facet directly manipulates storage, so ensure correct initialization and that no other facets are attempting to write to the same storage slot concurrently. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index a87a6448..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,222 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "ERC-721 NFT enumerableburn facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 NFT enumerableburn facet for Compose diamonds - - - -- Enables the burning of ERC721 tokens, effectively destroying them. -- Integrates with enumeration logic, ensuring burned tokens are removed from tracking. -- Supports the Compose diamond pattern for modularity and upgradeability. - - -## Overview - -The ERC721EnumerableBurnFacet extends ERC721 functionality by enabling the burning of NFTs. It integrates with the diamond's storage pattern to ensure token destruction is correctly reflected in enumeration tracking, maintaining the integrity of the NFT collection. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableBurn } from "../interfaces/IERC721EnumerableBurn.sol"; -import { Diamond } from "@openzeppelin/contracts/diamond/Diamond.sol"; - -contract ERC721EnumerableBurnDeployer { - address internal diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 _tokenId) external { - IERC721EnumerableBurn(diamondAddress).burn(_tokenId); - } -}`} - - -## Best Practices - - -- Initialize the facet's storage correctly during diamond deployment to ensure proper enumeration tracking. -- Ensure the `burn` function is called with valid token IDs that exist within the contract's state. -- Understand that burning a token removes it permanently and affects the total token count and enumeration order. - - -## Security Considerations - - -The `burn` function requires careful access control to prevent unauthorized token destruction. Ensure that only the token owner or an authorized entity can call this function. Reentrancy is not a concern as the function does not make external calls. Input validation on `_tokenId` is crucial to prevent errors when attempting to burn non-existent tokens. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx b/website/docs/contracts/facets/ERC721EnumerableFacet.mdx deleted file mode 100644 index 1d9d5615..00000000 --- a/website/docs/contracts/facets/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,753 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "ERC-721 NFT enumerable facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 NFT enumerable facet for Compose diamonds - - - -- Full ERC721 compliance with enumerable extensions, allowing iteration over all tokens and tokens owned by an address. -- Supports both direct and safe transfers, including checks for receiver contract compatibility. -- Provides essential NFT metadata functions like `name`, `symbol`, and `tokenURI`. -- Manages token ownership, supply, and approval states. - - -## Overview - -The ERC721EnumerableFacet provides a complete implementation of the ERC721 non-fungible token standard, including enumerable extensions. It handles token metadata, ownership tracking, approvals, and transfers, enabling a Compose diamond to act as a robust NFT collection. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondProxy, DiamondInit} from "@compose-protocol/diamond-proxy/contracts/DiamondProxy.sol"; -import {DiamondCut} from "@compose-protocol/diamond-proxy/contracts/DiamondCut.sol"; -import {ERC721EnumerableFacet} from "./ERC721EnumerableFacet.sol"; - -contract DeployDiamond { - function deploy() public { - // Facet implementations - ERC721EnumerableFacet erc721Facet = new ERC721EnumerableFacet(); - - // Diamond Cut data - DiamondCut.FacetCut[] memory cut = new DiamondCut.FacetCut[](1); - cut[0] = DiamondCut.FacetCut({ - facetAddress: address(erc721Facet), - action: DiamondCut.Action.ADD, - functionSelectors: Diamond.getSelectors(erc721Facet) - }); - - // Diamond Init data (if applicable, e.g., for name, symbol) - // For simplicity, init is omitted here but would typically be called - // via DiamondInit.DiamondInitInterface(diamondProxyAddress).initFacets(initData) - - // Deploy Diamond Proxy - DiamondProxy diamondProxy = new DiamondProxy(cut); - - // Example: Interact with the ERC721 facet after deployment - ERC721EnumerableFacet(diamondProxy).mint("0xOwnerAddress", 1); // Assuming a mint function exists and is added - uint256 supply = ERC721EnumerableFacet(diamondProxy).totalSupply(); - address owner = ERC721EnumerableFacet(diamondProxy).ownerOf(1); - } -} - -// Helper to get selectors (typically in a separate Diamond contract or utility) -library Diamond { - function getSelectors(address _facet) internal pure returns (bytes4[] memory selectors) { - // Implementation omitted for brevity; typically uses type casting and interface detection - return new bytes4[](0); // Placeholder - } -} -`} - - -## Best Practices - - -- Initialize the ERC721EnumerableFacet with essential metadata such as name and symbol during diamond deployment to ensure proper identification of the NFT collection. -- Carefully manage access control for functions like `approve` and `setApprovalForAll`, ensuring only the token owner or approved addresses can perform these actions. -- When upgrading the diamond, ensure the storage layout of the ERC721EnumerableFacet remains compatible. Avoid removing or reordering existing state variables. - - -## Security Considerations - - -This facet implements standard ERC721 logic; security relies on correct implementation of access controls and transfer logic. `safeTransferFrom` provides a layer of security against incompatible receiver contracts. Reentrancy is mitigated by standard ERC721 patterns, but thorough auditing of any custom `mint` or external interactions is recommended. Ensure that `internalTransferFrom` is only called by authorized internal logic. - - -
- -
- - diff --git a/website/docs/contracts/facets/ERC721Facet.mdx b/website/docs/contracts/facets/ERC721Facet.mdx deleted file mode 100644 index 6591b8eb..00000000 --- a/website/docs/contracts/facets/ERC721Facet.mdx +++ /dev/null @@ -1,663 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "ERC-721 non-fungible token facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 non-fungible token facet for Compose diamonds - - - -- Full ERC-721 compliance, including core functions like `balanceOf`, `ownerOf`, `transferFrom`, and `approve`. -- Support for metadata retrieval via `tokenURI`. -- Includes `safeTransferFrom` variants for enhanced security during token transfers to potentially unknown recipient contracts. -- Internal `internalTransferFrom` function encapsulates core transfer logic, promoting code clarity and reuse within the facet. - - -## Overview - -The ERC721Facet provides a comprehensive implementation of the ERC-721 Non-Fungible Token standard within a Compose diamond. It manages token ownership, approvals, metadata, and transfers, enabling a diamond to act as a registry and issuer of unique digital assets. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose/contracts/src/facets/ERC721Facet.sol"; - -contract ERC721Consumer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function consumeERC721() external { - IERC721Facet erc721Facet = IERC721Facet(diamondAddress); - - string memory name = erc721Facet.name(); - string memory symbol = erc721Facet.symbol(); - uint256 balance = erc721Facet.balanceOf(address(this)); - // ... other ERC721 calls - } - - function transferMyToken(address to, uint256 tokenId) external { - IERC721Facet erc721Facet = IERC721Facet(diamondAddress); - erc721Facet.transferFrom(msg.sender, to, tokenId); - } -}`} - - -## Best Practices - - -- Initialize the ERC721Facet as part of the diamond deployment process, ensuring all necessary storage is correctly set up. -- When interacting with the ERC721Facet, always use the diamond's address as the target contract. -- Be mindful of gas costs associated with `safeTransferFrom` due to the on-chain checks for receiver compatibility. - - -## Security Considerations - - -Access control for functions like `approve` and `setApprovalForAll` is implicitly handled by the ERC-721 standard, requiring ownership or prior approval. The `safeTransferFrom` functions include checks to prevent accidental token loss when transferring to contracts that do not implement the ERC721Receiver interface. Reentrancy is mitigated by the standard ERC721 transfer patterns, where checks are performed before state changes and external calls. - - -
- -
- - diff --git a/website/docs/contracts/facets/ExampleDiamond.mdx b/website/docs/contracts/facets/ExampleDiamond.mdx deleted file mode 100644 index 7da3d669..00000000 --- a/website/docs/contracts/facets/ExampleDiamond.mdx +++ /dev/null @@ -1,129 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Diamond core facet for ERC-2535 implementation" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond core facet for ERC-2535 implementation - - - -- Implements ERC-2535 diamond proxy standard for modularity. -- Manages facet registration and function selector mapping for delegatecall routing. -- Supports diamond upgrades by adding, replacing, or removing facets. - - -## Overview - -The ExampleDiamond contract serves as the core implementation for an ERC-2535 compliant diamond proxy. It manages facet registration, function selector routing, and contract ownership, enabling modularity and upgradeability. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; -import {ExampleDiamond} from "@compose/diamond-contracts/contracts/ExampleDiamond.sol"; - -// Example deployment script snippet -// Assume diamondCutFacet is deployed and its address is known -// Assume facetsToDeploy is an array of {facetAddress, action, functionSelectors} structs - -function deployDiamond(address[] memory facetAddresses, bytes[] memory facetBytecodes, address _owner) external { - // This is a simplified representation. Actual deployment involves creating facets - // and then cutting them into the diamond. - - // Example: Deploying and cutting facets - // address[] memory facetAddresses = new address[](1); - // bytes[] memory facetBytecodes = new bytes[](1); - // facetAddresses[0] = address(new MyFacet()); // Replace MyFacet with actual facet contract - // facetBytecodes[0] = type(MyFacet).creation.runtimeBytecode; - - // ExampleDiamond exampleDiamond = new ExampleDiamond(); - // exampleDiamond.diamondCut(facetsToDeploy, address(0), ""); // Simplified cut call - // exampleDiamond.transferOwnership(_owner); -}`} - - -## Best Practices - - -- Initialize the diamond with essential facets during deployment, including ownership and access control mechanisms. -- Ensure all facets added to the diamond are properly registered with their function selectors to enable correct routing. -- Carefully manage ownership transfers to maintain control over diamond upgrades and facet management. - - -## Security Considerations - - -The `constructor` function initializes the diamond and sets the owner. Access to `diamondCut` and `transferOwnership` functions is implicitly protected by ownership checks within the diamond's access control logic (not explicitly defined in this snippet but standard for diamond proxies). Ensure that the owner address is a secure multisig or EOA. Reentrancy is generally mitigated by the diamond proxy pattern as calls are typically delegatecalled into facets, but facets themselves must be audited for reentrancy. - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerFacet.mdx b/website/docs/contracts/facets/OwnerFacet.mdx deleted file mode 100644 index 0e625356..00000000 --- a/website/docs/contracts/facets/OwnerFacet.mdx +++ /dev/null @@ -1,216 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "Ownership management facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Ownership management facet for Compose diamonds - - - -- Manages the single administrative owner of the diamond. -- Supports transferring ownership to a new address, including the ability to renounce ownership by setting the new owner to `address(0)`. -- Provides direct access to the owner's storage slot via `getStorage` for advanced inspection. - - -## Overview - -The OwnerFacet provides essential ownership management capabilities for a Compose diamond. It allows for retrieving the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet ensures a clear chain of control and administrative access within the diamond. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; - -contract OwnerConsumer { - IOwnerFacet internal ownerFacet; - - // Assume ownerFacet is initialized with the diamond's address - constructor(address _diamondAddress) { - ownerFacet = IOwnerFacet(_diamondAddress); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function transferControl(address _newOwner) external { - // Access control for transferOwnership would typically be implemented - // within the diamond's access control facet or by checking owner() here. - ownerFacet.transferOwnership(_newOwner); - } - - function abdicate() external { - // Ensure this contract is the current owner before renouncing - require(ownerFacet.owner() == address(this), "Not owner"); - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize the OwnerFacet with the diamond's address to interact with its ownership functions. -- Implement robust access control checks in calling facets or within the diamond's logic layer before invoking `transferOwnership` or `renounceOwnership`. -- When transferring ownership, ensure the `_newOwner` address is validated to prevent accidental lockouts. - - -## Security Considerations - - -The `transferOwnership` function is a critical administrative control. Ensure that calls to this function are protected by appropriate access control mechanisms (e.g., only callable by the current owner or a designated admin role). Renouncing ownership removes all administrative capabilities, making the contract effectively immutable from an administrative standpoint unless ownership is re-established through other means (which is not possible with this facet alone). - - -
- -
- - diff --git a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx b/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx deleted file mode 100644 index 8be6dc7c..00000000 --- a/website/docs/contracts/facets/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,292 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "Two-step ownership transfer facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Two-step ownership transfer facet for Compose diamonds - - - -- Two-step ownership transfer process for enhanced security. -- Explicit functions for viewing current and pending ownership states. -- Supports `renounceOwnership` to relinquish ownership. - - -## Overview - -The OwnerTwoStepsFacet manages contract ownership through a secure two-step transfer process. This facet provides functions to view the current and pending owner, initiate a transfer, and accept ownership, ensuring that ownership changes are deliberate and confirmed by both parties. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "@compose/contracts/src/facets/ownership/IOwnerTwoStepsFacet.sol"; -import {OwnerTwoStepsFacet} from "@compose/contracts/src/facets/ownership/OwnerTwoStepsFacet.sol"; - -contract OwnerSetup { - address public diamondAddress; - - // Assume diamondAddress is set during deployment - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function initiateOwnershipTransfer(address _newOwner) public { - IOwnerTwoStepsFacet(diamondAddress).transferOwnership(_newOwner); - } - - function acceptNewOwnership() public { - IOwnerTwoStepsFacet(diamondAddress).acceptOwnership(); - } - - function getCurrentOwner() public view returns (address) { - return IOwnerTwoStepsFacet(diamondAddress).owner(); - } - - function getPendingOwner() public view returns (address) { - return IOwnerTwoStepsFacet(diamondAddress).pendingOwner(); - } -}`} - - -## Best Practices - - -- Initialize ownership transfer using `transferOwnership` and confirm with `acceptOwnership` to prevent accidental ownership changes. -- Regularly check the `pendingOwner` to monitor ongoing ownership transfer requests. -- Integrate this facet into your diamond's access control strategy, ensuring critical functions are protected by ownership. - - -## Security Considerations - - -Ensure that only the current owner can call `transferOwnership` and `renounceOwnership`. The `acceptOwnership` function should only be callable by the address designated as the pending owner. Direct calls to `getOwnerStorage` and `getPendingOwnerStorage` bypass the public getter functions and should be used with caution, primarily for internal facet logic or advanced debugging. - - -
- -
- - diff --git a/website/docs/contracts/facets/RoyaltyFacet.mdx b/website/docs/contracts/facets/RoyaltyFacet.mdx deleted file mode 100644 index aeb1b516..00000000 --- a/website/docs/contracts/facets/RoyaltyFacet.mdx +++ /dev/null @@ -1,195 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "ERC-2981 royalty facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2981 royalty facet for Compose diamonds - - - -- Implements ERC-2981 `royaltyInfo` function. -- Supports token-specific royalty configurations. -- Falls back to a default royalty percentage when token-specific settings are not found. -- Utilizes inline assembly for efficient storage access. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard, enabling diamonds to query royalty information for tokens. It provides a standardized interface for calculating royalty payments based on token-specific or default settings, facilitating revenue sharing for creators within the diamond ecosystem. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamond} from "@compose/diamond/contracts/IDiamond.sol"; -import {IRoyaltyFacet} from "@compose/diamond/contracts/facets/RoyaltyFacet.sol"; - -contract Deployer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getRoyaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address, uint256) { - // Get the RoyaltyFacet's address from the diamond - address royaltyFacetAddress = IDiamond(diamondAddress).getFacetAddress( - IRoyaltyFacet.ROYALTY_FACET_ID - ); - - // Call the royaltyInfo function on the RoyaltyFacet - return IRoyaltyFacet(royaltyFacetAddress).royaltyInfo(tokenId, salePrice); - } -}`} - - -## Best Practices - - -- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. -- Ensure the `STORAGE_POSITION` for royalty data is unique and managed by the diamond's storage contract. -- Access royalty storage via `getStorage` for read operations to maintain consistency. - - -## Security Considerations - - -The `royaltyInfo` function is view-only and does not modify state, mitigating reentrancy risks. Input validation for `tokenId` and `salePrice` is assumed to be handled by the caller or other facets. Ensure the `STORAGE_POSITION` is correctly defined and conflicts are avoided. - - -
- -
- - diff --git a/website/docs/contracts/facets/_category_.json b/website/docs/contracts/facets/_category_.json deleted file mode 100644 index 83c31a59..00000000 --- a/website/docs/contracts/facets/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Facets", - "position": 1, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/website/docs/contracts/interfaceDetection/ERC165/_category_.json b/website/docs/contracts/interfaceDetection/ERC165/_category_.json new file mode 100644 index 00000000..2f19715b --- /dev/null +++ b/website/docs/contracts/interfaceDetection/ERC165/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-165", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-165 components for Compose diamonds." + } +} diff --git a/website/docs/contracts/interfaceDetection/_category_.json b/website/docs/contracts/interfaceDetection/_category_.json new file mode 100644 index 00000000..08fc60ce --- /dev/null +++ b/website/docs/contracts/interfaceDetection/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Interface Detection", + "position": 5, + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "description": "ERC-165 interface detection support." + } +} diff --git a/website/docs/contracts/libraries/_category_.json b/website/docs/contracts/libraries/_category_.json new file mode 100644 index 00000000..0b5ad8cc --- /dev/null +++ b/website/docs/contracts/libraries/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Utilities", + "position": 4, + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "description": "Utility libraries and helpers for diamond development." + } +} diff --git a/website/docs/contracts/modules/AccessControlMod.mdx b/website/docs/contracts/modules/AccessControlMod.mdx deleted file mode 100644 index 1ac6c6a0..00000000 --- a/website/docs/contracts/modules/AccessControlMod.mdx +++ /dev/null @@ -1,450 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Role-based access control (RBAC) module for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control (RBAC) module for Compose diamonds - - - -- Supports granting and revoking roles for accounts. -- Allows checking role membership with `hasRole` and enforcing it with `requireRole`. -- Enables setting administrative roles for other roles, creating a hierarchy. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlMod provides robust role-based access control (RBAC) for Compose diamonds. It allows for granular permission management, ensuring that only authorized accounts can execute sensitive functions, which is critical for maintaining the integrity and security of the diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlMod} from "@compose/modules/AccessControlMod.sol"; - -contract MyFacet { - IAccessControlMod internal accessControlMod; - - // Assume AccessControlMod is initialized and accessible via the diamond proxy - function initialize(IAccessControlMod _accessControlMod) external { - accessControlMod = _accessControlMod; - } - - function grantAdminRole(address _account) external { - // Granting the default ADMIN_ROLE to an account - accessControlMod.grantRole(IAccessControlMod.ADMIN_ROLE, _account); - } - - function onlyAdminCanDoThis() external { - // Requiring the caller to have the ADMIN_ROLE - accessControlMod.requireRole(IAccessControlMod.ADMIN_ROLE, msg.sender); - // ... perform admin-only action ... - } - - function canCallerDoThis() external view returns (bool) { - // Checking if the caller has a specific role - return accessControlMod.hasRole(IAccessControlMod.DEFAULT_ADMIN_ROLE, msg.sender); - } -}`} - - -## Best Practices - - -- Always use `requireRole` for critical functions to enforce access control, reverting with `AccessControlUnauthorizedAccount` on failure. -- Utilize `hasRole` for read-only checks or informational purposes, avoiding unnecessary reverts. -- Manage roles and their admins carefully, as incorrect configuration can lead to unintended access. - - -## Integration Notes - - -The AccessControlMod interacts with the diamond's storage to maintain role assignments and role admin configurations. Facets integrate by calling the module's functions through the diamond proxy. Changes to role assignments or role admin relationships are immediately reflected across all facets interacting with the module. It is crucial to ensure that the AccessControlMod is initialized correctly and that its storage is managed according to Compose's storage pattern guidelines to maintain compatibility and upgradeability. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlPausableMod.mdx b/website/docs/contracts/modules/AccessControlPausableMod.mdx deleted file mode 100644 index 93ed3cbc..00000000 --- a/website/docs/contracts/modules/AccessControlPausableMod.mdx +++ /dev/null @@ -1,388 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Role-based access control with pause functionality for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Role-based access control with pause functionality for Compose diamonds - - - -- Role-based access control enforced at the function level. -- Granular pausing of individual roles, allowing selective disabling of functionality. -- Prevention of unauthorized access or operations when a role is paused. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlPausable module provides robust role-based access control combined with the ability to pause specific roles. This is crucial for diamonds requiring granular control over operations and the flexibility to temporarily halt certain functionalities without impacting the entire contract, enhancing safety and upgradeability. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausable} from "@compose/contracts/modules/access/AccessControlPausable.sol"; - -contract MyFacet { - IAccessControlPausable private accessControlPausable; - - // Assuming the diamond interface and storage are accessible - // interface IDiamond { - // function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external payable; - // function getFacetFunctionSelectors(address _facet) external view returns (bytes4[] memory); - // } - - // Example of how a facet might initialize or interact with the module - function initialize(address _accessControlPausableAddress) external { - accessControlPausable = IAccessControlPausable(_accessControlPausableAddress); - } - - // Example function protected by role and pause state - function sensitiveOperation() external { - // Replace 'ROLE_ADMIN' with the actual role identifier - accessControlPausable.requireRoleNotPaused(msg.sender, "ROLE_ADMIN"); - // ... perform sensitive operation ... - } - - // Example of pausing a role (likely by an authorized entity) - function pauseAdminRole() external { - // Assuming the caller has the necessary permissions to pause - accessControlPausable.pauseRole(msg.sender, "ROLE_ADMIN"); - } - - // Example of unpausing a role - function unpauseAdminRole() external { - // Assuming the caller has the necessary permissions to unpause - accessControlPausable.unpauseRole(msg.sender, "ROLE_ADMIN"); - } -}`} - - -## Best Practices - - -- Always use `requireRoleNotPaused` to enforce both role membership and pause status before executing sensitive operations. -- Ensure that only authorized entities can call `pauseRole` and `unpauseRole` by implementing appropriate access control within your facet or relying on the diamond's governance. -- Handle the specific `AccessControlUnauthorizedAccount` and `AccessControlRolePaused` errors appropriately in your frontend or consuming contracts to provide clear user feedback. - - -## Integration Notes - - -This module interacts with the diamond's storage to manage role pause states. Facets can access this functionality through the `IAccessControlPausable` interface. The `requireRoleNotPaused` function implicitly checks for role existence and pause status, reverting with specific errors if checks fail. It's crucial to ensure the AccessControlPausable module is correctly initialized and its storage slot is managed within the diamond's overall storage layout. - - -
- -
- - diff --git a/website/docs/contracts/modules/AccessControlTemporalMod.mdx b/website/docs/contracts/modules/AccessControlTemporalMod.mdx deleted file mode 100644 index 12264d4f..00000000 --- a/website/docs/contracts/modules/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,479 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Time-limited role-based access control for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Time-limited role-based access control for Compose diamonds - - - -- Time-limited role assignments: Roles automatically expire after a specified timestamp. -- Automatic expiry checks: `requireValidRole` verifies both role existence and non-expiry. -- Explicit revocation: `revokeTemporalRole` allows for immediate removal of temporal roles. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControlTemporalMod provides time-limited role-based access control, ensuring that granted permissions expire automatically. This enhances diamond security by enforcing temporary access, reducing the risk of stale permissions persisting indefinitely. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; -import {AccessControlUnauthorizedAccount, AccessControlRoleExpired} from "@compose/modules/access-control-temporal/Errors.sol"; - -contract MyFacet { - IAccessControlTemporalMod public constant ACCESS_CONTROL_TEMPORAL_MOD = IAccessControlTemporalMod(address(0)); // Replace with actual deployed address - - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - function grantOperatorRole(address _account, uint64 _expiry) external { - ACCESS_CONTROL_TEMPORAL_MOD.grantRoleWithExpiry(OPERATOR_ROLE, _account, _expiry); - } - - function executeOperation() external { - // Check if the caller has a valid, non-expired OPERATOR_ROLE - ACCESS_CONTROL_TEMPORAL_MOD.requireValidRole(OPERATOR_ROLE, msg.sender); - - // Operation logic here - } - - function revokeOperatorRole(address _account) external { - ACCESS_CONTROL_TEMPORAL_MOD.revokeTemporalRole(OPERATOR_ROLE, _account); - } -}`} - - -## Best Practices - - -- Use `requireValidRole` to enforce time-bound access to critical functions, preventing unauthorized actions after a role's expiry. -- Carefully manage role expiry timestamps to ensure timely revocation and maintain the principle of least privilege. -- Handle `AccessControlUnauthorizedAccount` and `AccessControlRoleExpired` errors to provide clear feedback to users when access is denied due to missing or expired roles. - - -## Integration Notes - - -This module interacts with the diamond's storage. Facets should import and use the `IAccessControlTemporalMod` interface. The `grantRoleWithExpiry`, `revokeTemporalRole`, and `requireValidRole` functions operate on the temporal access control state managed by this module. Ensure that the deployment process correctly initializes and links this module to the diamond proxy. The module relies on underlying access control mechanisms for role management and adds temporal constraints. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondCutMod.mdx b/website/docs/contracts/modules/DiamondCutMod.mdx deleted file mode 100644 index 39b5c281..00000000 --- a/website/docs/contracts/modules/DiamondCutMod.mdx +++ /dev/null @@ -1,414 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Diamond upgrade (cut) module for ERC-2535 diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond upgrade (cut) module for ERC-2535 diamonds - - - -- Enables dynamic addition, replacement, and removal of facets, providing flexibility for diamond upgrades. -- Supports executing an arbitrary function call via `delegatecall` immediately after a cut operation, allowing for initialization or post-upgrade logic execution. -- Provides granular control over individual function selectors within facets. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCutMod provides essential functionalities for managing facets within an ERC-2535 compliant diamond proxy. It enables precise control over diamond upgrades, allowing developers to add, replace, or remove functions, ensuring a robust and flexible upgrade path for diamond applications. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-contracts/contracts/diamond/interfaces/IDiamondCut.sol"; -import {DiamondCutMod} from "@compose/diamond-contracts/contracts/modules/DiamondCutMod.sol"; - -contract MyFacetManager { - IDiamondCut public diamondCutFacet; - - constructor(address _diamondCutFacetAddress) { - diamondCutFacet = IDiamondCut(_diamondCutFacetAddress); - } - - /** - * @notice Adds a new function to the diamond. - * @param _facetAddress The address of the facet contract. - * @param _functionSelectors An array of function selectors to add. - */ - function addFacet(address _facetAddress, bytes4[] memory _functionSelectors) external { - diamondCutFacet.diamondCut(_getAddFacet(_facetAddress, _functionSelectors), address(0), \"\", \"\"); - } - - /** - * @notice Replaces an existing function in the diamond. - * @param _facetAddress The address of the new facet contract. - * @param _functionSelectors An array of function selectors to replace. - */ - function replaceFacet(address _facetAddress, bytes4[] memory _functionSelectors) external { - diamondCutFacet.diamondCut(_getReplaceFacet(_facetAddress, _functionSelectors), address(0), \"\", \"\"); - } - - /** - * @notice Removes functions from the diamond. - * @param _functionSelectors An array of function selectors to remove. - */ - function removeFacetFunctions(bytes4[] memory _functionSelectors) external { - diamondCutFacet.diamondCut(new IDiamondCut.FacetCut[](0), address(0), \"\", _functionSelectors); - } - - // Helper functions to format FacetCut structs for diamondCut - function _getAddFacet(address _facetAddress, bytes4[] memory _functionSelectors) internal pure returns (IDiamondCut.FacetCut[] memory) { - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: _facetAddress, - action: IDiamondCut.Action.ADD, - selectors: _functionSelectors - }); - return cuts; - } - - function _getReplaceFacet(address _facetAddress, bytes4[] memory _functionSelectors) internal pure returns (IDiamondCut.FacetCut[] memory) { - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: _facetAddress, - action: IDiamondCut.Action.REPLACE, - selectors: _functionSelectors - }); - return cuts; - } -} `} - - -## Best Practices - - -- Ensure that only authorized entities can call `diamondCut`, `addFunctions`, `removeFunctions`, and `replaceFunctions` to maintain control over diamond upgrades. -- Carefully review function selectors before adding, replacing, or removing them to prevent unintended loss of functionality or introduction of vulnerabilities. -- When replacing facets, ensure that the new facet's functions are compatible with existing state and logic to avoid breaking the diamond's overall functionality. - - -## Integration Notes - - -The DiamondCutMod interacts directly with the diamond proxy's storage to manage facet mappings. The `diamondCut` function modifies the internal registry of facets and their associated function selectors. Any changes made through `diamondCut`, `addFunctions`, `removeFunctions`, or `replaceFunctions` are immediately reflected in the diamond's behavior. The order of operations within `diamondCut` is critical; additions, replacements, and removals are processed sequentially. The `getStorage` function can be used to inspect the current state of facet mappings. - - -
- -
- - diff --git a/website/docs/contracts/modules/DiamondMod.mdx b/website/docs/contracts/modules/DiamondMod.mdx deleted file mode 100644 index 1a16e93c..00000000 --- a/website/docs/contracts/modules/DiamondMod.mdx +++ /dev/null @@ -1,234 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Diamond Library - Internal functions and storage for diamond proxy functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Diamond Library - Internal functions and storage for diamond proxy functionality. - - - -- `addFacets`: Enables programmatic addition of facets and their function selectors, restricted to deployment time. -- `diamondFallback`: Acts as the core dispatch mechanism, routing external calls to the correct facet. -- `getStorage`: Provides access to the diamond's internal storage, allowing facets to introspect or interact with shared state. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondMod library provides essential internal functions and storage management for the Compose diamond proxy. It facilitates the addition of facets during deployment and enables the diamond to route calls to the appropriate facet, ensuring modularity and composability. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondMod} from "@compose/diamond/contracts/DiamondMod.sol"; - -contract MyFacet { - DiamondMod internal diamondMod; - - constructor(address _diamondModAddress) { - diamondMod = DiamondMod(_diamondModAddress); - } - - /** - * @notice Example of calling getStorage from a facet. - */ - function readDiamondStorage() external view returns (bytes memory) { - return diamondMod.getStorage(); - } -}`} - - -## Best Practices - - -- Facet functions should be `external` or `internal` only, as per Compose guidelines. -- Use `addFacets` exclusively during diamond deployment to ensure stable function mappings. -- Handle potential `diamondFallback` reentrancy or execution errors appropriately within calling facets. - - -## Integration Notes - - -DiamondMod manages the core diamond proxy's function selector to facet address mappings. Facets interact with DiamondMod via its external functions. Changes made by `addFacets` are permanent for the lifetime of the diamond proxy. The `getStorage` function returns the raw storage of the diamond, which facets can interpret based on their understanding of the diamond's storage layout. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC1155Mod.mdx b/website/docs/contracts/modules/ERC1155Mod.mdx deleted file mode 100644 index f52791bd..00000000 --- a/website/docs/contracts/modules/ERC1155Mod.mdx +++ /dev/null @@ -1,618 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-1155 Token Receiver Interface - Interface for contracts that want to handle safe transfers of ERC-1155 tokens. - - - -- Supports both single and batch transfers, minting, and burning operations for maximum efficiency. -- Includes robust safe transfer logic with receiver validation, adhering to EIP-1155 standards for secure token handling. -- Provides functionality to manage token URIs, allowing for dynamic metadata configuration. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC1155Mod provides a robust implementation of the ERC-1155 Multi-Token Standard, enabling diamonds to manage fungible and non-fungible tokens efficiently. It ensures safe token transfers, batch operations, and URI management, crucial for complex token economies and composability. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; -import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; -import { ERC1155Mod } from "./ERC1155Mod.sol"; - -contract MyERC1155Facet is ERC1155Mod, IERC1155 { - // Storage for ERC1155Mod is managed by the diamond storage pattern. - - // Example: Minting tokens - function mintNewTokens(address to, uint256 id, uint256 amount) external { - // Assuming this facet has the necessary permissions to mint - mint(to, id, amount); - } - - // Example: Safe transfer - function transferMyTokens(address from, address to, uint256 id, uint256 amount, bytes calldata data) external { - // Assuming this facet has the necessary permissions and approvals - safeTransferFrom(from, to, id, amount, data); - } - - // Implement required IERC1155Receiver functions if this facet acts as a receiver - function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external override returns (bytes4) { - // Handle received ERC1155 tokens - return - bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")); - } - - function onERC1155BatchReceived(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external override returns (bytes4) { - // Handle received batch of ERC1155 tokens - return - bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")); - } - - // ... other ERC1155 functions like balanceOf, uri, etc. would be implemented here or in other facets ... -}`} - - -## Best Practices - - -- Ensure the facet has appropriate access control for minting, burning, and URI setting functions. Rely on the diamond's access control mechanism. -- When implementing `onERC1155Received` and `onERC1155BatchReceived`, validate `data` or other parameters as necessary to ensure safe handling of incoming tokens. -- Be mindful of gas costs for batch operations; encourage users to batch when feasible, but understand single operations might be necessary. - - -## Integration Notes - - -The ERC1155Mod interacts with a predefined storage slot within the diamond's storage layout. Facets using this module can access and modify the ERC-1155 state (balances, URIs, etc.) through the module's functions. The `getStorage` function provides direct access to the ERC1155 storage struct, allowing for advanced logic or direct manipulation if needed, though direct manipulation should be done with extreme care to maintain invariants. Facets should ensure they do not introduce conflicting state or logic that violates ERC-1155 invariants. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC165Mod.mdx b/website/docs/contracts/modules/ERC165Mod.mdx deleted file mode 100644 index 280e7436..00000000 --- a/website/docs/contracts/modules/ERC165Mod.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC165 — ERC-165 Standard Interface Detection Library - Provides internal functions and storage layout for ERC-165 interface detection. - - - -- Standardized ERC-165 interface detection across diamond facets. -- Centralized interface registration and querying logic. -- Enables external contract compatibility and discovery of diamond capabilities. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod module provides a standardized way for facets within a Compose diamond to implement and expose ERC-165 interface detection. This is crucial for composability, allowing external contracts to reliably query which interfaces a diamond supports without needing to know its specific facet implementation details. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC165, IERC165} from "@compose/modules/erc165/LibERC165.sol"; - -contract MyERC721Facet { - struct Storage { - LibERC165.ERC165Storage erc165; - // ... other ERC721 storage variables - } - - function initialize(Storage storage self) external { - LibERC165.registerInterface(self.erc165, type(IERC721).interfaceId); - } - - function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { - return LibERC165.supportsInterface(LibERC165.getStorage(address(this)), interfaceId); - } -}`} - - -## Best Practices - - -- Ensure `LibERC165.registerInterface` is called during facet initialization for all supported interfaces. -- Always use `LibERC165.supportsInterface` to check for interface support, leveraging the diamond's ERC165 state. -- Avoid directly manipulating the `erc165` storage variable; use the provided library functions. - - -## Integration Notes - - -The ERC165Mod relies on a dedicated `ERC165Storage` struct, which must be included in the main diamond storage layout or within individual facet storage structs. The `getStorage` function uses inline assembly to precisely locate this storage at a fixed slot, ensuring consistent access across all facets. Facets should call `LibERC165.registerInterface` during their initialization to populate the `ERC165Storage` with their supported interface IDs. Changes to the registered interfaces are immediately visible through `LibERC165.supportsInterface`. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20BridgeableMod.mdx b/website/docs/contracts/modules/ERC20BridgeableMod.mdx deleted file mode 100644 index 7f78a3ba..00000000 --- a/website/docs/contracts/modules/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,424 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "LibERC20Bridgeable — ERC-7802 Library" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Bridgeable — ERC-7802 Library - - - -- Enforces `trusted-bridge` role for cross-chain minting and burning operations. -- Provides helper functions to access underlying ERC20 and AccessControl storage slots. -- Designed for integration into Compose diamond proxies, leveraging the storage pattern. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20BridgeableMod provides cross-chain interoperability for ERC20 tokens within a Compose diamond. It enforces access control for bridging operations, ensuring only trusted entities can mint or burn tokens across different chains, enhancing the security and composability of your token deployments. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableMod} from "@compose/contracts/modules/erc20/ERC20BridgeableMod.sol"; - -contract MyFacet { - address internal diamondAddress; - - // Assume diamondAddress is set during deployment or initialization - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnForBridge(address _token, uint256 _amount) external { - // Call the crosschainBurn function through the diamond proxy - // The diamond proxy will route this to the ERC20BridgeableMod facet - IERC20BridgeableMod(diamondAddress).crosschainBurn(_token, _amount); - } - - function mintForBridge(address _token, address _to, uint256 _amount) external { - // Call the crosschainMint function through the diamond proxy - IERC20BridgeableMod(diamondAddress).crosschainMint(_token, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is correctly managed within the AccessControl facet to restrict who can call `crosschainBurn` and `crosschainMint`. -- Always verify the outcome of cross-chain operations on the destination chain, as this module only handles the minting/burning initiation. -- Use `checkTokenBridge` internally before initiating cross-chain transfers from trusted bridge addresses to prevent unauthorized calls. - - -## Integration Notes - - -The ERC20BridgeableMod interacts with diamond storage slots defined by Compose. Specifically, it reads from the AccessControl storage to verify the `trusted-bridge` role and the ERC20 storage for token-specific details. Facets interacting with this module should call its functions through the diamond proxy. The `getAccessControlStorage` and `getERC20Storage` functions return references to these storage areas, allowing other facets to inspect the state without direct storage slot manipulation. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20Mod.mdx b/website/docs/contracts/modules/ERC20Mod.mdx deleted file mode 100644 index 89dfc266..00000000 --- a/website/docs/contracts/modules/ERC20Mod.mdx +++ /dev/null @@ -1,424 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20 — ERC-20 Library - Provides internal functions and storage layout for ERC-20 token logic. - - - -- Provides essential ERC-20 functions: `mint`, `burn`, `transfer`, `transferFrom`, `approve`. -- Manages ERC-20 specific storage via `IERC20ModStorage`. -- Utilizes inline assembly in `getStorage` for direct access to the storage struct at a fixed slot. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod module provides a standardized, internal library for implementing ERC-20 token functionality within a Compose diamond. It manages ERC-20 specific storage and offers essential functions like minting, burning, transferring, and approving allowances, ensuring consistent and safe token operations across facets. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod, IERC20ModStorage} from "@compose/modules/ERC20Mod.sol"; - -contract MyERC20Facet { - IERC20Mod public erc20mod; - - function initialize(address _erc20ModAddress) external { - erc20mod = IERC20Mod(_erc20ModAddress); - } - - function mintTokens(address _to, uint256 _amount) external { - // Assuming access control is handled externally or by another facet - erc20mod.mint(_to, _amount); - } - - function transferTokens(address _from, address _to, uint256 _amount) external { - // Assuming access control and allowance checks are handled - erc20mod.transfer(_from, _to, _amount); - } - - function approveSpender(address _spender, uint256 _amount) external { - erc20mod.approve(msg.sender, _spender, _amount); - } -}`} - - -## Best Practices - - -- Ensure proper access control mechanisms are in place before calling mint or burn functions, as these modify critical token state. -- Always verify that the necessary allowances are set before calling `transferFrom` by using `getStorage` to inspect the allowance struct. -- Handle potential `ERC20TransferErrors` and `ERC20AllowanceErrors` gracefully in consuming facets. - - -## Integration Notes - - -The ERC20Mod relies on a dedicated storage slot for its `IERC20ModStorage` struct, which contains balances, allowances, and total supply. Facets interacting with ERC20Mod should use the `getStorage` function to obtain a pointer to this struct. This ensures that all ERC20 operations consistently read from and write to the correct storage location, maintaining the integrity of the ERC-20 state within the diamond. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC20PermitMod.mdx b/website/docs/contracts/modules/ERC20PermitMod.mdx deleted file mode 100644 index e31b051a..00000000 --- a/website/docs/contracts/modules/ERC20PermitMod.mdx +++ /dev/null @@ -1,282 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage" -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC20Permit — Library for ERC-2612 Permit Logic - Library for self-contained ERC-2612 permit and domain separator logic and storage - - - -- Implements EIP-712 compliant domain separator generation for secure signature verification. -- Provides a robust `permit` function to validate and process ERC-2612 signature approvals. -- Designed for seamless integration with Compose diamond storage patterns. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20PermitMod library provides essential logic for implementing ERC-2612 permit functionality within a Compose diamond. It manages domain separator calculation and permit validation, enabling gasless token approvals via EIP-712 signatures. This modular approach ensures consistent and secure permit handling across diamond facets. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC20Permit} from "@compose/contracts/src/modules/erc20/LibERC20Permit.sol"; -import {ERC20PermitStorage} from "@compose/contracts/src/modules/erc20/ERC20PermitStorage.sol"; -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; - -contract MyERC20PermitFacet { - using LibERC20Permit for ERC20PermitStorage; - - ERC20PermitStorage private _permitStorage; - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Ensure the permit storage is initialized via the diamond initializer - // For example: _permitStorage = ERC20PermitStorage(LibDiamond.diamondStorage().erc20PermitStorage); - - // Call the library function to validate and set allowance - // The emit Approval event must be handled by the calling facet. - _permitStorage.permit(owner, spender, value, deadline, v, r, s); - } - - // Example of accessing domain separator (usually done by external signers) - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _permitStorage.DOMAIN_SEPARATOR(); - } -}`} - - -## Best Practices - - -- Ensure `ERC20PermitStorage` is properly initialized by the diamond's initializer function before any calls to `permit` or `DOMAIN_SEPARATOR` are made. -- The calling facet is responsible for emitting the `Approval` event as specified by ERC-20 and ERC-2612 standards after a successful `permit` call. -- Always verify the `deadline` provided in the permit signature to prevent stale approvals. - - -## Integration Notes - - -The ERC20PermitMod library relies on the `ERC20PermitStorage` struct. This struct must be included in the diamond's overall storage layout and initialized correctly. The `LibERC20Permit` functions operate directly on this storage. The `DOMAIN_SEPARATOR` function computes its value based on the contract address and chain ID, and this value is stored within the `ERC20PermitStorage`. Facets using this module will interact with `ERC20PermitStorage` via the library functions. The `permit` function itself does not emit the `Approval` event; this is a responsibility delegated to the calling facet to maintain explicit control over event emissions. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC6909Mod.mdx b/website/docs/contracts/modules/ERC6909Mod.mdx deleted file mode 100644 index 16debd53..00000000 --- a/website/docs/contracts/modules/ERC6909Mod.mdx +++ /dev/null @@ -1,528 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibERC6909 — ERC-6909 Library - Provides internal functions and storage layout for ERC-6909 minimal multi-token logic. - - - -- Provides foundational logic for ERC-6909 token management, including minting, burning, transfers, and approvals. -- Leverages the diamond storage pattern for efficient and upgradeable state management. -- Supports operator functionality for delegated token management. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC6909Mod module provides the core logic and storage for implementing the ERC-6909 minimal multi-token standard within a Compose diamond. It enables efficient management of multiple token types, including minting, burning, transferring, and approvals, all while adhering to the diamond's storage pattern for composability and upgradeability. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Mod} from "./IERC6909Mod.sol"; -import {LibDiamond} from "./diamond/LibDiamond.sol"; - -contract MyERC6909Facet { - uint256 constant ERC6909_STORAGE_SLOT = 1; // Example slot - - struct ERC6909Storage { - mapping(uint256 => mapping(address => uint256)) allowances; - mapping(uint256 => mapping(address => uint256)) balances; - mapping(address => mapping(address => bool)) operators; - } - - function _getERC6909Storage() internal pure returns (ERC6909Storage storage) { - return LibDiamond.getStorage(ERC6909_STORAGE_SLOT); - } - - function mintToken(uint256 _id, uint256 _amount, address _to) external { - ERC6909Storage storage erc6909 = _getERC6909Storage(); - // Call the internal mint function from the ERC6909Mod library - IERC6909Mod.mint(erc6909, _id, _amount, _to); - } - - function transferToken(uint256 _id, uint256 _amount, address _from, address _to) external { - ERC6909Storage storage erc6909 = _getERC6909Storage(); - // Call the internal transfer function from the ERC6909Mod library - IERC6909Mod.transfer(erc6909, _id, _amount, _from, _to); - } -}`} - - -## Best Practices - - -- Ensure the `ERC6909Mod` library is correctly integrated into the diamond's facet registry and storage layout. -- Implement proper access control for functions that modify state (e.g., `mint`, `burn`, `setOperator`) within your facets. -- Be mindful of `type(uint256).max` allowances and operator logic during transfers to prevent unintended state changes. - - -## Integration Notes - - -The ERC6909Mod library interacts with a dedicated storage slot within the diamond's global storage. Facets that implement ERC-6909 functionality must correctly reference this storage slot, typically via a `LibDiamond.getStorage()` call, to access and manipulate the `ERC6909Storage` struct. The ordering of storage variables within the `ERC6909Storage` struct is critical and must not be altered to maintain backward compatibility and correct state access across diamond upgrades. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721EnumerableMod.mdx b/website/docs/contracts/modules/ERC721EnumerableMod.mdx deleted file mode 100644 index c3ee77bc..00000000 --- a/website/docs/contracts/modules/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,347 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Enumerable Library for Compose - Provides internal logic for enumerable ERC-721 tokens using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. - - - -- Manages the addition and removal of tokens from internal enumeration lists during minting, burning, and transfers. -- Utilizes inline assembly via `getStorage` to efficiently access and manipulate the ERC-721 enumerable storage struct from its designated diamond storage slot. -- Enforces basic validation such as non-zero receiver addresses for minting and token existence checks for burn/transfer operations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721EnumerableMod module provides essential internal logic for managing enumerable ERC-721 tokens within a Compose diamond. It ensures that minted, burned, and transferred tokens are correctly tracked in enumeration lists, maintaining the integrity of token ownership and order. This is crucial for features like listing all tokens or tracking token indices. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod} from "@compose/modules/ERC721/ERC721EnumerableMod.sol"; - -contract MyERC721Facet { - // Assume ERC721EnumerableMod is deployed and its address is known - IERC721EnumerableMod private constant _erc721Enumerable = IERC721EnumerableMod(0x...); - - function mintToken(address _to, uint256 _tokenId) external { - // Before minting, ensure token does not exist (using ERC721 core logic, not shown here) - // ... - _erc721Enumerable.mint(_to, _tokenId); - // ... - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - // Perform ownership and approval checks (using ERC721 core logic, not shown here) - // ... - _erc721Enumerable.transferFrom(_from, _to, _tokenId); - // ... - } - - function burnToken(uint256 _tokenId) external { - // Perform ownership checks (using ERC721 core logic, not shown here) - // ... - _erc721Enumerable.burn(_tokenId); - // ... - } -}`} - - -## Best Practices - - -- Always perform necessary ERC-721 ownership and approval checks within your facet before calling `transferFrom` or `burn` from this module. -- Ensure the `mint` function is called with a valid, non-zero receiver address and that the token ID does not already exist to prevent state corruption. -- Handle potential reverts from this module gracefully, providing clear error messages to users. - - -## Integration Notes - - -The ERC721EnumerableMod interacts directly with the diamond's storage. The `getStorage` function, using inline assembly, retrieves a pointer to the `ERC721EnumerableStorage` struct located at a predefined storage slot within the diamond. Facets using this module must be aware that `mint`, `burn`, and `transferFrom` directly modify this shared storage, impacting the enumeration order and count of all ERC-721 tokens managed by the diamond. The ordering of storage variables within the `ERC721EnumerableStorage` struct is critical and must be preserved if the struct is extended. - - -
- -
- - diff --git a/website/docs/contracts/modules/ERC721Mod.mdx b/website/docs/contracts/modules/ERC721Mod.mdx deleted file mode 100644 index 60e25bf5..00000000 --- a/website/docs/contracts/modules/ERC721Mod.mdx +++ /dev/null @@ -1,354 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-721 Library for Compose - Provides internal logic for ERC-721 token management using diamond storage. This library is intended to be used by custom facets to integrate with ERC-721 functionality. - - - -- Provides atomic operations for minting, transferring, and burning ERC-721 tokens. -- Abstracts direct diamond storage access for ERC-721 state, simplifying facet development. -- Enforces ERC-721 standard invariants through its internal logic. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod module provides essential internal logic for managing ERC-721 tokens within a Compose diamond. It enables custom facets to implement ERC-721 functionality by abstracting direct interaction with diamond storage, promoting code reuse and adherence to the standard. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod} from "@compose/modules/ERC721Mod.sol"; - -contract MyERC721Facet { - IERC721Mod public constant ERC721Mod = IERC721Mod(address(0)); // Replace with actual diamond address - - function mintToken(address _to, uint256 _tokenId) external { - ERC721Mod.mint(_to, _tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - ERC721Mod.transferFrom(_from, _to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - ERC721Mod.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721Mod` contract address is correctly set or imported within facets that utilize its functions. -- Validate all input parameters, especially addresses and token IDs, before calling module functions to prevent unexpected reverts. -- Understand that this module directly manipulates diamond storage; any changes to its internal storage layout require careful consideration for upgrade compatibility. - - -## Integration Notes - - -The ERC721Mod interacts with diamond storage at a predefined slot to manage ERC-721 token ownership, approvals, and metadata. Facets using this module should be aware that any changes to the internal storage structure of ERC721Mod (e.g., adding new fields to its storage struct) may require diamond upgrades and careful migration strategies to maintain state continuity. The `getStorage` function allows direct access to this internal storage if needed by advanced facets. - - -
- -
- - diff --git a/website/docs/contracts/modules/NonReentrancyMod.mdx b/website/docs/contracts/modules/NonReentrancyMod.mdx deleted file mode 100644 index 9ddf87d2..00000000 --- a/website/docs/contracts/modules/NonReentrancyMod.mdx +++ /dev/null @@ -1,142 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibNonReentrancy - Non-Reentrancy Library - Provides common non-reentrant functions for Solidity contracts. - - - -- Provides `enter()` and `exit()` functions to manage reentrancy locks. -- Designed for integration into custom facets or as a library function using `using for`. -- Aids in maintaining state consistency by preventing recursive function calls within a single transaction context. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod provides essential utilities to prevent reentrant calls within your diamond facets. By implementing reentrancy guards, it ensures the integrity and security of your contract's state during complex interactions, which is crucial for maintaining predictable execution flows in a composable diamond architecture. - ---- - -## Storage - -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose/core/src/modules/NonReentrancyMod.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 internal _reentrancyGuard; - - /** - * @notice Performs an action that must be non-reentrant. - */ - function sensitiveAction() external { - // Enter the non-reentrancy guard. - _reentrancyGuard.enter(); - - try { - // Perform sensitive operations here. - // ... - } finally { - // Exit the non-reentrancy guard. - _reentrancyGuard.exit(); - } - } -}`} - - -## Best Practices - - -- Always ensure `exit()` is called, even if an error occurs during the protected operation. Use `try...finally` blocks for robust error handling. -- Store the reentrancy guard variable within your facet's storage, following Compose's storage pattern guidelines. -- Consider the implications of reentrancy when designing interactions between different facets; this module provides a fundamental safeguard. - - -## Integration Notes - - -The `NonReentrancyMod` is typically integrated by adding a `uint256` variable to the facet's storage to act as the reentrancy guard. This variable is then managed by the `enter()` and `exit()` functions. Facets that wish to use this module should include it in their storage layout and apply the library functions to their designated guard variable. The guard variable's state (locked or unlocked) is local to the facet using it. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerMod.mdx b/website/docs/contracts/modules/OwnerMod.mdx deleted file mode 100644 index a0cc51f4..00000000 --- a/website/docs/contracts/modules/OwnerMod.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Contract Ownership - Provides internal functions and storage layout for owner management. - - - -- Manages contract ownership according to ERC-173 standards. -- Provides atomic `requireOwner()` check for immediate access control. -- Supports explicit `transferOwnership()` for secure transitions. -- Allows retrieval of owner address via `owner()` and direct storage access via `getStorage()`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod module provides essential ERC-173 compliant contract ownership management for Compose diamonds. It defines storage for the contract owner and offers functions to retrieve, check, and update ownership, ensuring that critical operations can be restricted to authorized addresses within the diamond. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "../modules/OwnerMod.sol"; - -contract MyFacet { - uint256 constant OWNER_MOD_STORAGE_SLOT = 0x1234567890abcdef; // Example slot - - function initialize(address initialOwner) external { - IOwnerMod(OWNER_MOD_STORAGE_SLOT).setContractOwner(initialOwner); - } - - function changeOwner(address newOwner) external { - IOwnerMod(OWNER_MOD_STORAGE_SLOT).transferOwnership(newOwner); - } - - function getContractOwner() external view returns (address) { - return IOwnerMod(OWNER_MOD_STORAGE_SLOT).owner(); - } - - function onlyOwnerAction() external { - IOwnerMod(OWNER_MOD_STORAGE_SLOT).requireOwner(); - // Perform owner-specific action - } -}`} - - -## Best Practices - - -- Use `requireOwner()` judiciously for critical administrative functions to enforce access control based on contract ownership. -- Ensure the `OwnerMod` is initialized with the correct initial owner during diamond deployment or upgrade. -- Handle ownership transfers carefully; renouncing ownership by setting the new owner to `address(0)` should be a deliberate action. - - -## Integration Notes - - -The `OwnerMod` utilizes a dedicated storage slot (defined by `STORAGE_POSITION` within the module's implementation) to store its ownership-related state. Facets interact with this module by casting the storage slot address to the `IOwnerMod` interface. Any facet can call `setContractOwner`, `transferOwnership`, `owner`, or `requireOwner` as long as the `OwnerMod` facet is deployed to the diamond and the caller has the correct permissions or context to interact with the diamond's storage. - - -
- -
- - diff --git a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx b/website/docs/contracts/modules/OwnerTwoStepsMod.mdx deleted file mode 100644 index cd623fd7..00000000 --- a/website/docs/contracts/modules/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,318 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-173 Two-Step Contract Ownership Library - Provides two-step ownership transfer logic for facets or modular contracts. - - - -- Implements a secure two-step ownership transfer process, mitigating risks of accidental ownership loss. -- Provides `owner()` and `pendingOwner()` view functions for transparent state inspection. -- Includes `requireOwner()` for enforcing access control on sensitive operations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerTwoStepsMod provides a secure, two-step ownership transfer mechanism, crucial for managing diamond proxies and their facets. This pattern prevents accidental loss of control by requiring explicit acceptance of ownership, enhancing safety and composability within the diamond architecture. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {OwnerTwoStepsMod} from "../modules/OwnerTwoStepsMod.sol"; - -contract MyFacet is OwnerTwoStepsMod { - // Assuming OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are defined - // and initialized correctly within the diamond deployment. - - /** - * @notice Transfers ownership to a new address. - * @param _newOwner The address to transfer ownership to. - */ - function initiateOwnershipTransfer(address _newOwner) external { - transferOwnership(_newOwner); - } - - /** - * @notice Accepts an incoming ownership transfer. - */ - function acceptNewOwnership() external { - acceptOwnership(); - } - - /** - * @notice Renounces current ownership. - */ - function giveUpOwnership() external { - renounceOwnership(); - } - - /** - * @notice Returns the current owner of the diamond. - */ - function getCurrentOwner() external view returns (address) { - return owner(); - } - - /** - * @notice Returns the pending owner of the diamond. - */ - function getPendingOwnerAddress() external view returns (address) { - return pendingOwner(); - } -} -`} - - -## Best Practices - - -- Always call `transferOwnership` from the current owner and `acceptOwnership` from the pending owner to ensure a valid two-step transfer. -- Use `requireOwner` to protect critical administrative functions within your facets. -- Be aware that `renounceOwnership` permanently removes owner privileges; use with extreme caution. - - -## Integration Notes - - -This module interacts with diamond storage by reading and writing to specific storage slots designated for the owner and pending owner. Facets integrating this module must ensure that `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are correctly defined and initialized within the diamond's storage layout. Changes to these storage variables are immediately visible to all facets interacting with the diamond. - - -
- -
- - diff --git a/website/docs/contracts/modules/RoyaltyMod.mdx b/website/docs/contracts/modules/RoyaltyMod.mdx deleted file mode 100644 index c91d7e39..00000000 --- a/website/docs/contracts/modules/RoyaltyMod.mdx +++ /dev/null @@ -1,364 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic." -gitSource: "https://github.com/maxnorm/Compose/blob/8fcbb812c7f4f59338db6b04ce17631a729a30f9/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -LibRoyalty - ERC-2981 Royalty Standard Library - Provides internal functions and storage layout for ERC-2981 royalty logic. - - - -- Implements ERC-2981 `royaltyInfo` standard for on-chain royalty queries. -- Supports both default royalties applicable to all tokens and token-specific overrides. -- Utilizes a dedicated storage slot for efficient access to royalty data via inline assembly. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The RoyaltyMod module provides essential ERC-2981 compliant royalty logic for Compose diamonds. It enables setting and querying default and token-specific royalties, ensuring creators are compensated on secondary sales. This module is crucial for marketplaces and NFT platforms built on Compose. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "../interfaces/IRoyaltyMod.sol"; - -contract RoyaltyFacet { - // Assume IRoyaltyMod is correctly interfaced and the diamond proxy is implemented - IRoyaltyMod internal royaltyMod; - - constructor(address _diamondProxyAddress) { - royaltyMod = IRoyaltyMod(_diamondProxyAddress); - } - - /** - * @notice Sets a default royalty for all tokens. - * @param _receiver The address to receive royalty payments. - * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). - */ - function grantDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setDefaultRoyalty(_receiver, _feeBasisPoints); - } - - /** - * @notice Sets a specific royalty for a token ID. - * @param _tokenId The ID of the token to set royalty for. - * @param _receiver The address to receive royalty payments. - * @param _feeBasisPoints The royalty fee in basis points (e.g., 1000 for 10%). - */ - function grantTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); - } - - /** - * @notice Queries royalty information for a given token and sale price. - * @param _tokenId The ID of the token. - * @param _salePrice The total sale price of the token. - * @return receiver The address to receive royalty payments. - * @return feeAmount The calculated royalty amount. - */ - function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 feeAmount) { - (receiver, feeAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); - return (receiver, feeAmount); - } -} -`} - - -## Best Practices - - -- Ensure the `_receiver` address is validated to prevent sending royalties to unintended accounts. -- Use `deleteDefaultRoyalty` or `resetTokenRoyalty` judiciously, understanding that `royaltyInfo` will then return `(address(0), 0)` for tokens without specific royalty settings. -- Be aware that setting token-specific royalties overrides default settings; ensure this behavior aligns with your application's requirements. - - -## Integration Notes - - -The RoyaltyMod library interacts with diamond storage by reading and writing to a predefined storage slot managed by the diamond proxy. Facets using this module will call its functions, which in turn access and modify the shared royalty storage. The `getStorage` function provides direct access to the `RoyaltyStorage` struct, which contains `defaultRoyalty` and `tokenRoyalties` mappings. Changes made through `setDefaultRoyalty` and `setTokenRoyalty` are immediately visible to subsequent calls to `royaltyInfo`. - - -
- -
- - diff --git a/website/docs/contracts/modules/_category_.json b/website/docs/contracts/modules/_category_.json deleted file mode 100644 index 134b5727..00000000 --- a/website/docs/contracts/modules/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules", - "position": 2, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/website/docs/contracts/token/ERC1155/_category_.json b/website/docs/contracts/token/ERC1155/_category_.json new file mode 100644 index 00000000..012a98bd --- /dev/null +++ b/website/docs/contracts/token/ERC1155/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-1155", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-1155 multi-token implementations." + } +} diff --git a/website/docs/contracts/token/ERC20/ERC20/_category_.json b/website/docs/contracts/token/ERC20/ERC20/_category_.json new file mode 100644 index 00000000..0b74f444 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 fungible token implementations." + } +} diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json new file mode 100644 index 00000000..74afd31f --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Bridgeable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 Bridgeable extension for ERC-20 tokens." + } +} diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json b/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json new file mode 100644 index 00000000..54093a31 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Permit", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 Permit extension for ERC-20 tokens." + } +} diff --git a/website/docs/contracts/token/ERC20/_category_.json b/website/docs/contracts/token/ERC20/_category_.json new file mode 100644 index 00000000..0b74f444 --- /dev/null +++ b/website/docs/contracts/token/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 fungible token implementations." + } +} diff --git a/website/docs/contracts/token/ERC6909/ERC6909/_category_.json b/website/docs/contracts/token/ERC6909/ERC6909/_category_.json new file mode 100644 index 00000000..5653b1ef --- /dev/null +++ b/website/docs/contracts/token/ERC6909/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-6909 minimal multi-token implementations." + } +} diff --git a/website/docs/contracts/token/ERC6909/_category_.json b/website/docs/contracts/token/ERC6909/_category_.json new file mode 100644 index 00000000..5653b1ef --- /dev/null +++ b/website/docs/contracts/token/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-6909 minimal multi-token implementations." + } +} diff --git a/website/docs/contracts/token/ERC721/ERC721/_category_.json b/website/docs/contracts/token/ERC721/ERC721/_category_.json new file mode 100644 index 00000000..5fdbd55a --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-721 non-fungible token implementations." + } +} diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json new file mode 100644 index 00000000..6ab22b34 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721 Enumerable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-721 Enumerable extension for ERC-721 tokens." + } +} diff --git a/website/docs/contracts/token/ERC721/_category_.json b/website/docs/contracts/token/ERC721/_category_.json new file mode 100644 index 00000000..5fdbd55a --- /dev/null +++ b/website/docs/contracts/token/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-721 non-fungible token implementations." + } +} diff --git a/website/docs/contracts/token/Royalty/_category_.json b/website/docs/contracts/token/Royalty/_category_.json new file mode 100644 index 00000000..95a86a02 --- /dev/null +++ b/website/docs/contracts/token/Royalty/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Royalty", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-2981 royalty standard implementations." + } +} diff --git a/website/docs/contracts/token/_category_.json b/website/docs/contracts/token/_category_.json new file mode 100644 index 00000000..4de378d1 --- /dev/null +++ b/website/docs/contracts/token/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Token Standards", + "position": 3, + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "description": "Token standard implementations for Compose diamonds." + } +} From 79890baf2d2272a6da79ea4efd2b99eef9de6cfc Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sat, 20 Dec 2025 03:06:52 +0000 Subject: [PATCH 40/68] docs: auto-generate docs pages from NatSpec --- .../AccessControl/AccessControlFacet.mdx | 560 +++++++++++++ .../access/AccessControl/AccessControlMod.mdx | 443 +++++++++++ .../AccessControlPausableFacet.mdx | 368 +++++++++ .../AccessControlPausableMod.mdx | 386 +++++++++ .../AccessControlTemporalFacet.mdx | 447 +++++++++++ .../AccessControlTemporalMod.mdx | 473 +++++++++++ .../contracts/access/Owner/OwnerFacet.mdx | 210 +++++ .../docs/contracts/access/Owner/OwnerMod.mdx | 273 +++++++ .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 290 +++++++ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 298 +++++++ .../contracts/diamond/DiamondCutFacet.mdx | 416 ++++++++++ .../docs/contracts/diamond/DiamondCutMod.mdx | 387 +++++++++ .../contracts/diamond/DiamondLoupeFacet.mdx | 252 ++++++ website/docs/contracts/diamond/DiamondMod.mdx | 236 ++++++ .../diamond/example/ExampleDiamond.mdx | 139 ++++ .../interfaceDetection/ERC165/ERC165Mod.mdx | 157 ++++ .../contracts/libraries/NonReentrancyMod.mdx | 138 ++++ .../contracts/token/ERC1155/ERC1155Facet.mdx | 664 ++++++++++++++++ .../contracts/token/ERC1155/ERC1155Mod.mdx | 605 ++++++++++++++ .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 246 ++++++ .../token/ERC20/ERC20/ERC20Facet.mdx | 569 ++++++++++++++ .../contracts/token/ERC20/ERC20/ERC20Mod.mdx | 422 ++++++++++ .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 443 +++++++++++ .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 420 ++++++++++ .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 339 ++++++++ .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 291 +++++++ .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 538 +++++++++++++ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 517 ++++++++++++ .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 209 +++++ .../token/ERC721/ERC721/ERC721Facet.mdx | 662 ++++++++++++++++ .../token/ERC721/ERC721/ERC721Mod.mdx | 358 +++++++++ .../ERC721EnumerableBurnFacet.mdx | 225 ++++++ .../ERC721EnumerableFacet.mdx | 742 ++++++++++++++++++ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 348 ++++++++ .../contracts/token/Royalty/RoyaltyFacet.mdx | 193 +++++ .../contracts/token/Royalty/RoyaltyMod.mdx | 356 +++++++++ 36 files changed, 13620 insertions(+) create mode 100644 website/docs/contracts/access/AccessControl/AccessControlFacet.mdx create mode 100644 website/docs/contracts/access/AccessControl/AccessControlMod.mdx create mode 100644 website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx create mode 100644 website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx create mode 100644 website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx create mode 100644 website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx create mode 100644 website/docs/contracts/access/Owner/OwnerFacet.mdx create mode 100644 website/docs/contracts/access/Owner/OwnerMod.mdx create mode 100644 website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx create mode 100644 website/docs/contracts/diamond/DiamondCutFacet.mdx create mode 100644 website/docs/contracts/diamond/DiamondCutMod.mdx create mode 100644 website/docs/contracts/diamond/DiamondLoupeFacet.mdx create mode 100644 website/docs/contracts/diamond/DiamondMod.mdx create mode 100644 website/docs/contracts/diamond/example/ExampleDiamond.mdx create mode 100644 website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx create mode 100644 website/docs/contracts/libraries/NonReentrancyMod.mdx create mode 100644 website/docs/contracts/token/ERC1155/ERC1155Facet.mdx create mode 100644 website/docs/contracts/token/ERC1155/ERC1155Mod.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx create mode 100644 website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx create mode 100644 website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx create mode 100644 website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx create mode 100644 website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx create mode 100644 website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx create mode 100644 website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx create mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx create mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx create mode 100644 website/docs/contracts/token/Royalty/RoyaltyFacet.mdx create mode 100644 website/docs/contracts/token/Royalty/RoyaltyMod.mdx diff --git a/website/docs/contracts/access/AccessControl/AccessControlFacet.mdx b/website/docs/contracts/access/AccessControl/AccessControlFacet.mdx new file mode 100644 index 00000000..91613086 --- /dev/null +++ b/website/docs/contracts/access/AccessControl/AccessControlFacet.mdx @@ -0,0 +1,560 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Manages roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages roles and permissions within a diamond. + + + +- Role-based access control (RBAC) for granular permission management. +- Support for granting and revoking roles to individual accounts or batches. +- Ability to define and manage admin roles for other roles. + + +## Overview + +The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling administrators to grant and revoke roles to specific accounts, ensuring that only authorized users can perform sensitive operations. This facet is fundamental for securing diamond functionalities. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; +import {DiamondLoupeFacet} from "@compose/diamond/contracts/facets/DiamondLoupeFacet.sol"; +import {AccessControlFacet} from "@compose/diamond/contracts/facets/AccessControlFacet.sol"; + +contract MyDiamond is IDiamondCut { + address constant ACCESS_CONTROL_FACET_ADDRESS = address(0x123...); // Deployed AccessControlFacet address + + function upgradeAndAddAccessControl() external payable { + // ... other upgrade logic ... + FacetCut[] memory cuts = new FacetCut[](1); + cuts[0] = FacetCut({ + facetAddress: ACCESS_CONTROL_FACET_ADDRESS, + action: FacetCutAction.Add, + functionSelectors: + AccessControlFacet.getStorage.selector ^ + AccessControlFacet.hasRole.selector ^ + AccessControlFacet.requireRole.selector ^ + AccessControlFacet.getRoleAdmin.selector ^ + AccessControlFacet.setRoleAdmin.selector ^ + AccessControlFacet.grantRole.selector ^ + AccessControlFacet.revokeRole.selector ^ + AccessControlFacet.grantRoleBatch.selector ^ + AccessControlFacet.revokeRoleBatch.selector ^ + AccessControlFacet.renounceRole.selector + }); + diamondCut(cuts, address(0), ""); + } + + // Example of calling a function that requires a role + function sensitiveOperation() external { + AccessControlFacet(address(this)).requireRole( + AccessControlFacet.DEFAULT_ADMIN_ROLE(), // Example role + msg.sender + ); + // ... perform sensitive operation ... + } +} +`} + + +## Best Practices + + +- Initialize roles and grant necessary permissions during deployment or upgrade processes. +- Designate a specific role (e.g., `DEFAULT_ADMIN_ROLE`) for managing other roles and permissions. +- Use `requireRole` judiciously to protect critical functions, ensuring only authorized accounts can execute them. + + +## Security Considerations + + +Ensure that the caller has the necessary administrative role before calling functions like `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch` to prevent unauthorized privilege escalation. The `renounceRole` function allows accounts to give up their own roles, which should be used with caution. Input validation is implicitly handled by the `requireRole` checks and role admin checks. + + +
+ +
+ + diff --git a/website/docs/contracts/access/AccessControl/AccessControlMod.mdx b/website/docs/contracts/access/AccessControl/AccessControlMod.mdx new file mode 100644 index 00000000..b93780f6 --- /dev/null +++ b/website/docs/contracts/access/AccessControl/AccessControlMod.mdx @@ -0,0 +1,443 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Manage roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within a diamond. + + + +- Standardized role-based access control for diamond applications. +- Functions for granting, revoking, and checking role assignments. +- Ability to define and manage administrative roles for other roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControl module provides a standardized way to manage roles and permissions for accounts interacting with a Compose diamond. It ensures that sensitive functions can only be called by authorized entities, enhancing the security and integrity of the diamond's operations. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose/modules/access-control/IAccessControl.sol"; + +contract AccessControlFacet { + IAccessControl internal accessControl; + + constructor(address _diamondAddress) { + accessControl = IAccessControl(_diamondAddress); + } + + function grantAdminRole(address _account) external { + bytes32 adminRole = accessControl.getStorage().adminRole; + accessControl.grantRole(adminRole, _account); + } + + function checkAccess() external view { + bytes32 someRole = accessControl.getStorage().defaultAdminRole; // Example role + requireRole(someRole, msg.sender); + } +}`} + + +## Best Practices + + +- Use `requireRole` to enforce access control checks directly within facet functions, reverting with `AccessControlUnauthorizedAccount` if unauthorized. +- Define and manage roles using `setRoleAdmin` to establish a clear hierarchy for role management. +- Grant roles judiciously; consider the principle of least privilege when assigning roles to accounts. + + +## Integration Notes + + +The AccessControl module utilizes its own storage slot within the diamond's storage layout. Facets can access this storage via the `getStorage()` function. Changes made to role assignments or role admin configurations are immediately reflected across all facets interacting with the diamond proxy. + + +
+ +
+ + diff --git a/website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..c83da868 --- /dev/null +++ b/website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -0,0 +1,368 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Manage roles and pausing functionality within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and pausing functionality within a diamond. + + + +- Role-based pausing: Temporarily disable specific roles. +- Admin-controlled operations: Pause and unpause actions are restricted to role admins. +- Reverts on paused roles: Automatically prevents execution when a role is paused. + + +## Overview + +This facet provides robust access control and pausing capabilities for roles within a Compose diamond. It allows administrators to temporarily disable specific roles, preventing any account from utilizing functions associated with that role. This ensures critical operations can be halted safely during emergencies or maintenance. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {Diamond} from "@compose/diamond/Diamond.sol"; +import {AccessControlPausableFacet} from "@compose/access-control/AccessControlPausableFacet.sol"; + +contract MyDiamond is Diamond { + constructor(address _diamondAdmin, address[] memory _facetCuts) Diamond(_diamondAdmin, _facetCuts) {} + + function pauseMyRole() external { + address accessControlPausableFacet = getFacetAddress(AccessControlPausableFacet.getSelector("pauseRole(bytes32)")); + (bool success, bytes memory data) = address(this).delegatecall(abi.encodeWithSelector(AccessControlPausableFacet.getSelector("pauseRole(bytes32)"), MY_ROLE_HASH)); + require(success, "Failed to pause role"); + } + + function unpauseMyRole() external { + address accessControlPausableFacet = getFacetAddress(AccessControlPausableFacet.getSelector("unpauseRole(bytes32)")); + (bool success, bytes memory data) = address(this).delegatecall(abi.encodeWithSelector(AccessControlPausableFacet.getSelector("unpauseRole(bytes32)"), MY_ROLE_HASH)); + require(success, "Failed to unpause role"); + } +} +`} + + +## Best Practices + + +- Grant and manage roles using the `AccessControl` facet before deploying this facet. +- Only the designated admin of a role can pause or unpause it. +- Integrate `requireRoleNotPaused` checks within your facet functions that depend on specific roles. + + +## Security Considerations + + +Ensure that role administration is handled securely. The `pauseRole` and `unpauseRole` functions are only callable by the admin of the specific role, mitigating unauthorized pausing. The `requireRoleNotPaused` internal function must be used diligently within other facets to prevent execution when a role is paused, avoiding unexpected behavior or vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx new file mode 100644 index 00000000..cc68b5cd --- /dev/null +++ b/website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -0,0 +1,386 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Manage role-based pausing and unpausing of operations." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage role-based pausing and unpausing of operations. + + + +- Role-specific pausing: Allows individual roles to be paused independently. +- Emergency stop capability: Enables immediate cessation of operations tied to specific roles. +- Composable with Access Control: Leverages existing role structures for permissioning. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides granular control over role execution, allowing specific roles to be paused and unpaused. It integrates with the diamond's access control system to enforce pausing logic, ensuring that operations tied to a paused role cannot be executed. This is crucial for emergency stops or controlled maintenance. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableMod} from "@compose/modules/AccessControlPausableMod.sol"; + +contract MyFacet { + IAccessControlPausableMod public constant ACCESS_CONTROL_PAUSABLE_MOD = IAccessControlPausableMod(); + + address public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + address public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + + /** + * @notice Allows an operator to perform a sensitive action. + * @dev Reverts if the OPERATOR_ROLE is paused. + */ + function performSensitiveAction() external { + ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(OPERATOR_ROLE); + // ... perform action ... + } + + /** + * @notice Allows a pauser to pause the operator role. + * @dev Reverts if the PAUSER_ROLE is not active or if the role is already paused. + */ + function pauseOperator() external { + ACCESS_CONTROL_PAUSABLE_MOD.pauseRole(OPERATOR_ROLE); + } + + /** + * @notice Allows a pauser to unpause the operator role. + * @dev Reverts if the PAUSER_ROLE is not active or if the role is not paused. + */ + function unpauseOperator() external { + ACCESS_CONTROL_PAUSABLE_MOD.unpauseRole(OPERATOR_ROLE); + } +}`} + + +## Best Practices + + +- Integrate `requireRoleNotPaused` at the entry point of functions that should be protected by role-based pausing. +- Ensure proper role management (granting/revoking) occurs through a separate, secure mechanism before using `pauseRole` or `unpauseRole`. +- Handle `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` errors explicitly in consuming facets or client applications. + + +## Integration Notes + + +This module utilizes the standard Compose diamond storage pattern. Facets interact with it by calling its external functions. The module's state is managed within its own storage slots, distinct from other facets. The `requireRoleNotPaused` function checks both role membership and the paused status of that role, ensuring that only authorized and active roles can execute protected functions. The module emits `RolePaused` and `RoleUnpaused` events upon state changes. + + +
+ +
+ + diff --git a/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..e3878165 --- /dev/null +++ b/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -0,0 +1,447 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Manages time-bound role assignments and checks for temporal access control." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages time-bound role assignments and checks for temporal access control. + + + +- Grants roles with specific expiry timestamps. +- Checks for role expiry and reverts if a role is no longer valid. +- Admin-controlled role granting and revocation with temporal constraints. + + +## Overview + +This facet extends Compose's access control by introducing time-bound role assignments. It allows administrators to grant roles with specific expiry dates and provides mechanisms to check if a role is still valid or has expired. This is crucial for scenarios requiring temporary permissions or scheduled access revocation. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet} from "@compose/diamond/facets/DiamondCutFacet.sol"; +import {AccessControlTemporalFacet} from "@compose/diamond/facets/AccessControlTemporalFacet.sol"; + +contract MyDiamond is DiamondCutFacet { + // ... constructor and other facets ... + + function grantTempAdminRole(address _account, uint64 _expiryTimestamp) external { + AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(address(this)); + bytes32 adminRole = getRoleAdmin(DEFAULT_ADMIN_ROLE); // Assuming DEFAULT_ADMIN_ROLE is defined + temporalFacet.grantRoleWithExpiry(adminRole, _account, _expiryTimestamp); + } + + function checkTempRole(address _account, bytes32 _role) external view { + AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(address(this)); + if (temporalFacet.isRoleExpired(_role, _account)) { + revert("Role has expired"); + } + // Further checks or logic... + } +} +`} + + +## Best Practices + + +- Grant temporal roles only to trusted accounts and set appropriate expiry durations. +- Regularly audit temporal role assignments to ensure they align with current access needs. +- Utilize the `requireValidRole` function within other facets to enforce time-bound access checks. + + +## Security Considerations + + +Ensure that the caller invoking `grantRoleWithExpiry` and `revokeTemporalRole` is indeed the administrator of the role to prevent unauthorized role manipulation. The expiry timestamp should be carefully managed to avoid accidental permanent denial of access or prolonged excessive permissions. + + +
+ +
+ + diff --git a/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..c93654dc --- /dev/null +++ b/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -0,0 +1,473 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Manages role assignments with time-based expirations." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages role assignments with time-based expirations. + + + +- Grants roles with specific expiry timestamps, enabling time-limited access. +- Provides `isRoleExpired` to check the status of a role assignment. +- Reverts with specific errors (`AccessControlRoleExpired`) when expired roles are used. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module extends standard access control by allowing roles to be granted with specific expiration timestamps. It ensures that only currently valid role assignments are considered, enhancing the security and flexibility of role-based permissions within a diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/AccessControlTemporalMod.sol"; + +contract MyFacet { + IAccessControlTemporalMod internal accessControlTemporalMod; + + function initialize(address _accessControlTemporalMod) public { + accessControlTemporalMod = IAccessControlTemporalMod(_accessControlTemporalMod); + } + + function grantAdminRoleTemporarily(address _user, uint64 _expiry) public { + accessControlTemporalMod.grantRoleWithExpiry(bytes32(0x0), _user, _expiry); // Assuming role 0x0 is 'admin' + } + + function checkAdmin(address _user) public view { + accessControlTemporalMod.requireValidRole(bytes32(0x0), _user); + } +}`} + + +## Best Practices + + +- Use `grantRoleWithExpiry` to set time-bound permissions, and `revokeTemporalRole` to proactively remove them early. +- Always check role validity with `requireValidRole` before executing sensitive operations. +- Be mindful of the `AccessControlRoleExpired` error, which indicates a role has expired and access should be denied. + + +## Integration Notes + + +The AccessControlTemporalMod integrates with the diamond's storage pattern. It manages its own storage, distinct from other modules. Facets interacting with this module should use the provided interface functions. The `requireValidRole` function enforces temporal validity, preventing the use of expired roles. + + +
+ +
+ + diff --git a/website/docs/contracts/access/Owner/OwnerFacet.mdx b/website/docs/contracts/access/Owner/OwnerFacet.mdx new file mode 100644 index 00000000..c275b66c --- /dev/null +++ b/website/docs/contracts/access/Owner/OwnerFacet.mdx @@ -0,0 +1,210 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Manages contract ownership and transfer." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership and transfer. + + + +- Provides standard ERC-173 ownership functions. +- Enables programmatic ownership management via the diamond proxy. + + +## Overview + +The OwnerFacet provides essential ownership management functions for a Compose diamond. It allows for retrieving the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative functions within the diamond. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; + +contract OwnerManager { + IOwnerFacet ownerFacet; + + constructor(address _diamondAddress) { + ownerFacet = IOwnerFacet(_diamondAddress); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function transferContractOwnership(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function renounceContractOwnership() external { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize the owner during diamond deployment to a trusted address. +- Use `transferOwnership` to delegate control to another address, ensuring the new owner is verified before the transfer completes. +- Only use `renounceOwnership` if explicit administrative control is no longer required. + + +## Security Considerations + + +Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Ensure the owner's private key is secured to prevent unauthorized changes. Setting the owner to `address(0)` effectively renounces ownership, making administrative functions permissionless unless other facets impose further restrictions. + + +
+ +
+ + diff --git a/website/docs/contracts/access/Owner/OwnerMod.mdx b/website/docs/contracts/access/Owner/OwnerMod.mdx new file mode 100644 index 00000000..6bd655b1 --- /dev/null +++ b/website/docs/contracts/access/Owner/OwnerMod.mdx @@ -0,0 +1,273 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "Manages ERC-173 contract ownership and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-173 contract ownership and transfers. + + + +- Manages ERC-173 compliant contract ownership. +- Provides `owner`, `transferOwnership`, and `requireOwner` functions for robust control. +- Supports ownership renouncement by transferring to `address(0)`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerMod provides essential ERC-173 compliant ownership management for your diamond. It defines a clear owner and provides functions to safely transfer ownership, ensuring control remains with the designated address. This module is fundamental for securing upgradeability and administrative functions within a diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/contracts/modules/owner/OwnerMod.sol"; +import {IDiamondProxy} from "@compose/contracts/diamond/IDiamondProxy.sol"; + +contract MyFacet { + IDiamondProxy public diamondProxy; + IOwnerMod ownerMod; + + constructor(address _diamondProxyAddress) { + diamondProxy = IDiamondProxy(_diamondProxyAddress); + // Assumes OwnerMod is registered and accessible via the diamond proxy + ownerMod = IOwnerMod(address(this)); + } + + /** + * @notice Get the current owner of the diamond. + */ + function getCurrentOwner() external view returns (address) { + return ownerMod.owner(); + } + + /** + * @notice Transfer ownership of the diamond to a new address. + * @param _newOwner The address of the new owner. + */ + function transferDiamondOwnership(address _newOwner) external { + // Access the OwnerMod through the diamond proxy interface + ownerMod.transferOwnership(_newOwner); + } + + /** + * @notice Renounce ownership of the diamond. + */ + function renounceDiamondOwnership() external { + ownerMod.transferOwnership(address(0)); + } + + /** + * @notice Check if the caller is the owner. + */ + function assertCallerIsOwner() external view { + ownerMod.requireOwner(); + } +}`} + + +## Best Practices + + +- Ensure the OwnerMod is correctly initialized and registered within the diamond proxy during deployment. +- Always use `transferOwnership` for owner changes; do not attempt to directly manipulate storage. +- Handle the `OwnerUnauthorizedAccount` error gracefully in external calls that require ownership. + + +## Integration Notes + + +The OwnerMod stores ownership data in a dedicated storage slot. Facets can access this data via the `IOwnerMod` interface. The `getStorage` function provides a direct pointer to the internal storage struct, allowing for low-level access if necessary, though standard function calls are preferred for safety and clarity. Any facet can call `owner`, `requireOwner`, and `transferOwnership` through the diamond proxy. + + +
+ +
+ + diff --git a/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..3bfb7ca0 --- /dev/null +++ b/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -0,0 +1,290 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Manages contract ownership with a two-step transfer process." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership with a two-step transfer process. + + + +- Two-step ownership transfer for enhanced security. +- Prevents accidental ownership changes. +- Provides clear functions to view current and pending owners. + + +## Overview + +This facet implements a secure, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are intentional by requiring both the current owner to initiate a transfer and the new owner to accept it, preventing accidental or unauthorized takeovers. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "@compose-protocol/diamond-contracts/contracts/facets/owner/IOwnerTwoStepsFacet.sol"; + +contract OwnerManager { + IOwnerTwoStepsFacet public immutable ownerFacet; + + constructor(address _ownerFacetAddress) { + ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); + } + + function startOwnershipTransfer(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function acceptNewOwnership() external { + ownerFacet.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function getPendingOwner() external view returns (address) { + return ownerFacet.pendingOwner(); + } +}`} + + +## Best Practices + + +- Initialize ownership transfer by calling `transferOwnership` from the current owner. +- The new owner must call `acceptOwnership` to finalize the transfer. +- Use `owner()` and `pendingOwner()` to track ownership status. + + +## Security Considerations + + +Access control is critical: only the current owner can initiate a transfer, and only the pending owner can accept it. Ensure the `transferOwnership` function is protected by appropriate access controls if called by other contract logic. The `OwnerUnauthorizedAccount` error is emitted if non-authorized accounts attempt to call sensitive functions. + + +
+ +
+ + diff --git a/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..219287f9 --- /dev/null +++ b/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -0,0 +1,298 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "Manages contract ownership with a secure two-step transfer process." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership with a secure two-step transfer process. + + + +- Secure two-step ownership transfer to prevent accidental or unauthorized changes. +- Explicit `acceptOwnership` function for the pending owner. +- `renounceOwnership` function to permanently remove owner privileges. +- Provides `owner()` and `pendingOwner()` view functions for state inspection. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module implements a two-step ownership transfer mechanism, enhancing security by requiring explicit acceptance from the new owner. It provides essential functions for managing contract ownership, including transferring, accepting, and renouncing ownership, ensuring that administrative control changes are deliberate and auditable. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoSteps} from "../interfaces/IOwnerTwoSteps.sol"; + +contract MyFacet { + IOwnerTwoSteps private immutable _ownerTwoSteps; + + constructor(address ownerTwoStepsAddress) { + _ownerTwoSteps = IOwnerTwoSteps(ownerTwoStepsAddress); + } + + function _requireOwner() internal view { + _ownerTwoSteps.requireOwner(); + } + + function transferAdminOwnership(address _newOwner) external { + _requireOwner(); + _ownerTwoSteps.transferOwnership(_newOwner); + } + + function acceptAdminOwnership() external { + _ownerTwoSteps.acceptOwnership(); + } +}`} + + +## Best Practices + + +- Always call `transferOwnership` from the current owner and `acceptOwnership` from the pending owner. +- Use `renounceOwnership` cautiously, as it permanently relinquishes administrative control. +- Ensure the `OwnerTwoStepsMod` facet is deployed and accessible before attempting any ownership operations. + + +## Integration Notes + + +The `OwnerTwoStepsMod` stores ownership-related state in dedicated storage slots. Facets interacting with this module should be aware of the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` constants if they need to directly access or reference the storage pointers using inline assembly. The `requireOwner` function enforces access control, ensuring that only the current owner can execute sensitive administrative functions. + + +
+ +
+ + diff --git a/website/docs/contracts/diamond/DiamondCutFacet.mdx b/website/docs/contracts/diamond/DiamondCutFacet.mdx new file mode 100644 index 00000000..4820e0dc --- /dev/null +++ b/website/docs/contracts/diamond/DiamondCutFacet.mdx @@ -0,0 +1,416 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Manage diamond facets and functions." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and functions. + + + +- Allows adding, replacing, and removing functions and facets from the diamond proxy. +- Supports executing an initialization function call as part of a diamond cut operation. +- Provides functions to retrieve ownership and diamond storage pointers for inspection. + + +## Overview + +The DiamondCutFacet provides essential functions for managing the diamond's upgradeability and facet composition. It allows adding, replacing, and removing functions and facets, along with direct control over diamond upgrades. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-proxy/contracts/facets/IDiamondCut.sol"; + +contract Deployer { + // Assume diamondProxy is an instance of the diamond proxy contract + IDiamondCut diamondProxy; + + constructor(address _diamondProxyAddress) { + diamondProxy = IDiamondCut(_diamondProxyAddress); + } + + function upgradeDiamond(address _facetAddress, bytes4[] memory _selectors, address _initAddress, bytes memory _calldata) public { + // Example: Adding a new facet + diamondProxy.diamondCut([(_facetAddress, 3, _selectors)], _initAddress, _calldata); + } + + function replaceExistingFacet(address _newFacetAddress, bytes4[] memory _selectors) public { + // Example: Replacing functions on an existing facet + diamondProxy.diamondCut([(_newFacetAddress, 2, _selectors)], address(0), ""); + } + + function removeFacetFunctions(address _facetAddress, bytes4[] memory _selectors) public { + // Example: Removing functions from a facet + diamondProxy.diamondCut([(_facetAddress, 1, _selectors)], address(0), ""); + } +}`} + + +## Best Practices + + +- Use `diamondCut` for all facet and function modifications to maintain a consistent upgrade path. +- Ensure initialization functions are correctly specified and revert appropriately when necessary to prevent state corruption. +- Carefully manage the `DiamondCutFacet`'s ownership or access control to prevent unauthorized upgrades. + + +## Security Considerations + + +The `diamondCut` function is a powerful administrative function. Access control must be strictly enforced to prevent unauthorized upgrades. Ensure that facet addresses provided are valid and that selectors are correctly mapped. Reentrancy is not a concern as the function is not designed for reentrant calls. Initialization functions should be carefully audited for security vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/contracts/diamond/DiamondCutMod.mdx b/website/docs/contracts/diamond/DiamondCutMod.mdx new file mode 100644 index 00000000..4d9c1146 --- /dev/null +++ b/website/docs/contracts/diamond/DiamondCutMod.mdx @@ -0,0 +1,387 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Manage diamond facets and function selectors." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and function selectors. + + + +- Atomically adds, replaces, or removes multiple facet-selector mappings in a single transaction. +- Supports optional delegatecall execution of an initialization function after the cut. +- Includes robust error handling for invalid operations and reverted initializations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCut module provides the core functionality for managing facets within a Compose diamond. It allows for the addition, replacement, and removal of functions, enabling dynamic upgrades and feature composition. This module is crucial for evolving diamond functionality while maintaining a single on-chain contract address. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-contracts/contracts/modules/DiamondCutMod.sol"; +import {IFacetA} from "./IFacetA.sol"; // Example facet interface + +contract MyDiamondConsumer { + IDiamondCut public constant DIAMOND_CUT = IDiamondCut(0x1234567890abcdef1234567890abcdef12345678); // Replace with actual diamond address + + function upgradeFacetA(address _newFacetAAddress, bytes4[] memory _selectors) external { + // Define facet cut data for FacetA + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: _newFacetAAddress, + action: IDiamondCut.FacetCutAction.REPLACE, + selectors: _selectors + }); + + // Perform the diamond cut + // Note: In a real scenario, you would likely need to be authorized to call diamondCut. + DIAMOND_CUT.diamondCut(cuts, address(0), ""); + } + + function callFacetA(bytes4 _selector, bytes memory _calldata) external returns (bytes memory) { + // Call a function on FacetA via the diamond proxy + (bool success, bytes memory result) = address(DIAMOND_CUT).call(abi.encodeWithSelector( + _selector, // Selector for the function in FacetA + _calldata // Calldata for the function + )); + require(success, "FacetA call failed"); + return result; + } +}`} + + +## Best Practices + + +- Ensure the caller is authorized to perform diamond cut operations, as this is a privileged action. +- Carefully manage facet addresses and selectors to prevent accidental removal of essential functionality or introduction of malicious code. +- Always test diamond upgrades thoroughly in a staging environment before deploying to production. + + +## Integration Notes + + +The DiamondCut module interacts directly with the diamond proxy's storage to update the mapping of selectors to facet addresses. Any changes made via `diamondCut` are immediately reflected in the proxy's routing logic. Facets should be designed to be stateless or manage their state independently, as the diamond proxy itself does not store facet-specific data beyond the selector-to-facet mapping. Ensure that any new facets added or replaced are compatible with the diamond's overall architecture and any existing storage patterns. + + +
+ +
+ + diff --git a/website/docs/contracts/diamond/DiamondLoupeFacet.mdx b/website/docs/contracts/diamond/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..e89080de --- /dev/null +++ b/website/docs/contracts/diamond/DiamondLoupeFacet.mdx @@ -0,0 +1,252 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "Query diamond facets and their associated function selectors." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Query diamond facets and their associated function selectors. + + + +- Provides read-only access to the diamond's facet registry. +- Supports querying individual facet addresses by function selector. +- Returns a structured list of all facets and their associated selectors. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, their addresses, and the specific function selectors each facet handles. This is crucial for understanding the diamond's internal structure and for building tools or dapps that interact with it. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupe} from "@compose/diamond/contracts/facets/DiamondLoupeFacet.sol"; + +contract DiamondUser { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getDiamondFacets() public view returns (IDiamondLoupe.Facet[] memory) { + IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); + return loupe.facets(); + } + + function getFacetAddrForSelector(bytes4 _selector) public view returns (address) { + IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); + return loupe.facetAddress(_selector); + } +}`} + + +## Best Practices + + +- Integrate this facet into your diamond to enable runtime inspection of its deployed facets and their function mappings. +- Use the `facets()` function to retrieve a comprehensive list of all facets and their selectors for auditing or tooling purposes. +- Utilize `facetAddress(_selector)` to determine which facet handles a specific function call, aiding in debugging and external contract interactions. + + +## Security Considerations + + +This facet is read-only and does not modify state, making it inherently safe from reentrancy and state corruption attacks. Access control is typically managed at the diamond proxy level, ensuring only authorized entities can modify the facet registry itself. + + +
+ +
+ + diff --git a/website/docs/contracts/diamond/DiamondMod.mdx b/website/docs/contracts/diamond/DiamondMod.mdx new file mode 100644 index 00000000..4a18a1f2 --- /dev/null +++ b/website/docs/contracts/diamond/DiamondMod.mdx @@ -0,0 +1,236 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Manages diamond facets and fallback logic" +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond facets and fallback logic + + + +- Manages facet registration and function selector mapping. +- Implements the diamond fallback mechanism for routing calls to appropriate facets. +- Supports adding multiple facets and their functions in a single operation during deployment. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondMod provides core logic for managing facets within a Compose diamond. It handles adding facets during deployment and routing external calls to the correct facet, ensuring composability and proper execution of diamond functions. This module is fundamental for extending diamond functionality. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondMod} from "@compose/contracts/diamond/interfaces/IDiamondMod.sol"; + +contract MyFacet { + IDiamondMod internal diamondMod; + + constructor(address _diamondMod) { + diamondMod = IDiamondMod(_diamondMod); + } + + /** + * @notice Example of calling a function through the diamond proxy. + */ + function callFacetFunction(bytes4 _selector, address _facetAddress) external returns (bytes memory) { + // In a real scenario, you would likely not pass the facet address directly + // but rather let diamondFallback resolve it. This is for demonstration. + return diamondMod.diamondFallback(_selector, _facetAddress, ""); + } +}`} + + +## Best Practices + + +- Facet additions are restricted to the initial diamond deployment phase. +- Ensure correct function selector mapping when adding facets to prevent routing issues. +- Handle potential errors such as `FunctionNotFound` or `CannotAddFunctionToDiamondThatAlreadyExists`. + + +## Integration Notes + + +DiamondMod interacts directly with the diamond proxy's storage to maintain mappings of function selectors to facet addresses. The `addFacets` function is designed to be called only during the initial deployment of the diamond. The `diamondFallback` function is crucial for the diamond proxy's operation, as it resolves incoming function calls to their respective facets. Any changes to facet mappings managed by this module are immediately reflected in the diamond's behavior. + + +
+ +
+ + diff --git a/website/docs/contracts/diamond/example/ExampleDiamond.mdx b/website/docs/contracts/diamond/example/ExampleDiamond.mdx new file mode 100644 index 00000000..49700de8 --- /dev/null +++ b/website/docs/contracts/diamond/example/ExampleDiamond.mdx @@ -0,0 +1,139 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Example Diamond contract for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Example Diamond contract for Compose diamonds + + + +- Supports initialization of the diamond with multiple facets. +- Manages the registration of function selectors to facet addresses for routing. +- Establishes the initial owner of the diamond contract. + + +## Overview + +This contract serves as a foundational example for Compose diamonds. It demonstrates the basic structure and initialization process for adding facets to a diamond proxy. Its primary purpose is to set up the diamond's initial state, including registering facets and assigning ownership. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondInit, FacetCut, FacetCutAction} from "@compose/diamond/contracts/DiamondInit.sol"; + +contract ExampleDiamondInit is DiamondInit { + function init(FacetCut[] memory _diamondCuts) public payable { + // Call the parent init function to process facet cuts + processCuts(_diamondCuts); + } + + // Example of how you might define cuts for initialization + function getExampleCuts() public pure returns (FacetCut[] memory) { + // Assume MyFacet and AnotherFacet are deployed and have their addresses + // and selectors defined. + FacetCut[] memory cuts = new FacetCut[](2); + + // Example cut for MyFacet + cuts[0] = FacetCut({ + facetAddress: address(0x123...\/\* MyFacet address *\/ ), + action: FacetCutAction.Add, + functionSelectors: new bytes4[](0) // Populate with actual selectors + }); + + // Example cut for AnotherFacet + cuts[1] = FacetCut({ + facetAddress: address(0x456...\/\* AnotherFacet address *\/ ), + action: FacetCutAction.Add, + functionSelectors: new bytes4[](0) // Populate with actual selectors + }); + + return cuts; + } +}`} + + +## Best Practices + + +- Initialize the diamond with all necessary facets and their selectors during deployment. +- Ensure the `owner` is set correctly to manage the diamond's upgradeability. +- Use explicit initializer functions rather than constructors for deployment flows. + + +## Security Considerations + + +The constructor grants significant control over the diamond's initial state and ownership. Ensure that the provided facet addresses are trusted and that the function selectors are correctly mapped to prevent unintended behavior. Access control for subsequent upgrades is managed by the owner. + + +
+ +
+ + diff --git a/website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx new file mode 100644 index 00000000..2ecf6039 --- /dev/null +++ b/website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx @@ -0,0 +1,157 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "Implement ERC-165 interface detection for diamond facets." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implement ERC-165 interface detection for diamond facets. + + + +- Standard ERC-165 interface detection. +- Internal library for easy integration into facets. +- Minimal storage footprint. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod provides the necessary storage and internal functions to implement the ERC-165 standard for interface detection within a diamond proxy. This allows other contracts to query which interfaces a diamond facet supports, enhancing composability and interoperability. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC165, IERC165} from "@compose/modules/erc165/LibERC165.sol"; + +contract MyERC721Facet { + struct Storage { + LibERC165.ERC165Storage erc165Storage; + // other facet storage variables + } + + function initialize(Storage storage self) external { + LibERC165.registerInterface(self.erc165Storage, type(IERC721).interfaceId); + } + + function supportsInterface(bytes4 interfaceId) external view override returns (bool) { + // Access storage via diamond storage layout + Storage storage self = Storage(address(this).code.bytes.offset(0)); + return LibERC165.supportsInterface(self.erc165Storage, interfaceId); + } +}`} + + +## Best Practices + + +- Call `registerInterface` during facet initialization to declare supported interfaces. +- Ensure the `ERC165Storage` struct is included in your facet's storage layout. +- Implement `supportsInterface` in facets that declare support for interfaces. + + +## Integration Notes + + +The ERC165Mod requires an `ERC165Storage` struct within each facet that implements ERC-165. This struct should be placed at the beginning of the facet's storage layout to ensure consistent slot allocation across upgrades. The `getStorage` function is used internally to bind to the correct storage slot. Facets should implement the `supportsInterface` function, calling `LibERC165.supportsInterface` to check registered interfaces. + + +
+ +
+ + diff --git a/website/docs/contracts/libraries/NonReentrancyMod.mdx b/website/docs/contracts/libraries/NonReentrancyMod.mdx new file mode 100644 index 00000000..6f010dd4 --- /dev/null +++ b/website/docs/contracts/libraries/NonReentrancyMod.mdx @@ -0,0 +1,138 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "Prevent reentrant calls within diamond functions." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Prevent reentrant calls within diamond functions. + + + +- Atomic execution guarantee: Prevents reentrant calls, ensuring functions complete without interruption. +- Simple integration: Utilizes a straightforward `enter`/`exit` pattern for easy adoption. +- Gas efficiency: Designed to minimize gas overhead associated with reentrancy protection. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod module provides essential functions to prevent reentrant function calls, safeguarding against reentrancy attacks. By integrating this module, developers ensure that sensitive operations within a diamond are executed atomically and without unintended recursive calls, maintaining state integrity. + +--- + +## Storage + +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/modules/NonReentrancyMod.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 internal _lock; + + function protectedAction() external { + // Enter the non-reentrant lock. The lock state is managed internally by the library. + _lock.enter(); + + // Perform sensitive operations here... + // If another external call tries to re-enter this function while it's executing, + // it will revert due to the active lock. + + // Exit the non-reentrant lock, allowing future calls. + _lock.exit(); + } +}`} + + +## Best Practices + + +- Use `_lock.enter()` at the beginning of functions vulnerable to reentrancy and `_lock.exit()` at the end, before any external calls or state changes that could trigger reentrancy. +- Ensure the `_lock` variable is correctly initialized and managed according to the library's expectations. Typically, this involves a storage slot dedicated to the reentrancy lock. +- Handle the `Reentrancy` error explicitly in calling facets to gracefully manage situations where a reentrant call is detected. + + +## Integration Notes + + +The `NonReentrancyMod` operates by managing a lock state, typically stored within a facet. The `enter` function sets the lock, and the `exit` function releases it. The library expects to be used with a `uint256` or equivalent type that can store the lock's state. Facets integrating this module must ensure that the storage slot used for the lock is appropriately managed and that the `enter` and `exit` functions are called in the correct sequence to maintain the reentrancy guard's integrity. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC1155/ERC1155Facet.mdx b/website/docs/contracts/token/ERC1155/ERC1155Facet.mdx new file mode 100644 index 00000000..a182a268 --- /dev/null +++ b/website/docs/contracts/token/ERC1155/ERC1155Facet.mdx @@ -0,0 +1,664 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "Manages ERC-1155 fungible and non-fungible tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 fungible and non-fungible tokens. + + + +- Supports both fungible and non-fungible tokens. +- Implements batch transfers for efficiency. +- Provides flexible URI resolution for token metadata. + + +## Overview + +The ERC1155Facet provides a comprehensive implementation for the ERC-1155 multi-token standard. It enables the management of fungible and non-fungible tokens within a Compose diamond, supporting batch transfers, approvals, and token URI resolution. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; +import {ERC1155Facet} from "@compose/contracts/src/facets/ERC1155/ERC1155Facet.sol"; + +contract ERC1155User { + IERC1155Facet public erc1155Facet; + + constructor(address _diamondAddress) { + erc1155Facet = IERC1155Facet(_diamondAddress); + } + + function getTokenUri(uint256 _id) public view returns (string memory) { + return erc1155Facet.uri(_id); + } + + function getBalance(address _account, uint256 _id) public view returns (uint256) { + return erc1155Facet.balanceOf(_account, _id); + } +}`} + + +## Best Practices + + +- Initialize the ERC1155 facet with a base URI if token URIs will be concatenated. +- Use `setApprovalForAll` to grant operator permissions for transfers. +- Store and manage token IDs and their associated data carefully to prevent mismatches. + + +## Security Considerations + + +Ensure that `safeTransferFrom` and `safeBatchTransferFrom` correctly validate recipient contract interfaces to prevent reentrancy or unexpected behavior. Access control for minting/burning functions (if implemented in other facets) must be robust. Batch operations require careful length validation for all input arrays to prevent logical errors. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC1155/ERC1155Mod.mdx b/website/docs/contracts/token/ERC1155/ERC1155Mod.mdx new file mode 100644 index 00000000..82d50b73 --- /dev/null +++ b/website/docs/contracts/token/ERC1155/ERC1155Mod.mdx @@ -0,0 +1,605 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "Manage ERC-1155 token transfers, minting, and burning within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-1155 token transfers, minting, and burning within a diamond. + + + +- Supports minting and burning of single or multiple ERC-1155 token types. +- Implements safe transfer logic for single and batch transfers, including receiver validation. +- Allows setting base and token-specific URIs for metadata. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides a comprehensive implementation for handling ERC-1155 tokens, enabling minting, burning, and safe transfers. It adheres to EIP-1155 standards, ensuring interoperability and secure token management. Integrating this module allows your diamond to function as an ERC-1155 token issuer and manager. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Mod} from "./interfaces/IERC1155Mod.sol"; + +contract MyDiamondFacet { + IERC1155Mod internal constant ERC1155 = IERC1155Mod(address(this)); + + function _mintTokens(address _to, uint256 _id, uint256 _amount) external { + ERC1155.mint(_to, _id, _amount); + } + + function _burnTokens(address _from, uint256 _id, uint256 _amount) external { + ERC1155.burn(_from, _id, _amount); + } + + function _safeTransfer(address _from, address _to, uint256 _id, uint256 _amount) external { + ERC1155.safeTransferFrom(_from, _to, _id, _amount, ""); + } + + function _setTokenURI(uint256 _tokenId, string memory _uri) external { + ERC1155.setTokenURI(_tokenId, _uri); + } +}`} + + +## Best Practices + + +- Implement access control for minting and burning functions to prevent unauthorized operations. +- Carefully validate receiver addresses in transfer functions to prevent sending tokens to invalid destinations. +- Ensure proper ERC1155Receiver interface implementation for contracts receiving tokens to handle safe transfers correctly. + + +## Integration Notes + + +This module utilizes a dedicated storage slot for its ERC-1155 state, including balances, approvals, and URI mappings. Facets interacting with this module can access its state via the `getStorage` function or by calling the module's functions directly through the diamond proxy. Changes to token balances or URIs are immediately reflected and visible to all facets. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx new file mode 100644 index 00000000..1492f69f --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -0,0 +1,246 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Burn ERC-20 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC-20 tokens within a Compose diamond. + + + +- Enables burning of ERC-20 tokens directly via the diamond proxy. +- Supports burning from the caller's balance and from an approved allowance. +- Emits `Transfer` events to the zero address upon successful burning, adhering to ERC-20 standards. + + +## Overview + +The ERC20BurnFacet allows burning of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account using their allowance, ensuring compliance with ERC-20 standards and emitting necessary events. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; + +contract ERC20BurnConsumer { + IERC20BurnFacet constant ERC20_BURN = IERC20BurnFacet(address(1)); // Replace with actual diamond address + + function consumeBurn(address _tokenAddress, uint256 _amount) external { + // Assuming ERC20BurnFacet is implemented for _tokenAddress + ERC20_BURN.burn(_amount); + } + + function consumeBurnFrom(address _tokenAddress, address _from, uint256 _amount) external { + // Assuming ERC20BurnFacet is implemented for _tokenAddress + ERC20_BURN.burnFrom(_from, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20BurnFacet is correctly deployed and selectors are added to the diamond proxy. +- Use the `burn` function to remove tokens from the caller's balance. +- Utilize `burnFrom` when an allowance has been granted to the caller for another user's tokens. + + +## Security Considerations + + +The `burn` and `burnFrom` functions require sufficient balance and allowance respectively. The `burnFrom` function relies on the ERC20 `approve` mechanism being correctly implemented and used by the token owner. Ensure adequate access control is applied at the diamond proxy level if certain users should not have access to these functions. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx new file mode 100644 index 00000000..ab448770 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx @@ -0,0 +1,569 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Implements the ERC-20 token standard on Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements the ERC-20 token standard on Compose diamonds. + + + +- Implements the full ERC-20 standard interface. +- Utilizes the Compose diamond storage pattern for state management. +- Supports token transfers, balance queries, and approval mechanisms. + + +## Overview + +The ERC20Facet provides standard ERC-20 token functionality, enabling tokens to be managed, transferred, and approved within a Compose diamond. It exposes core ERC-20 methods like `transfer`, `balanceOf`, and `approve`, integrating seamlessly with the diamond's storage pattern. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20Facet} from "@compose/contracts/src/facets/ERC20Facet.sol"; +import {DiamondProxy} from "@compose/contracts/src/DiamondProxy.sol"; +import {DiamondInit} from "@compose/contracts/src/DiamondInit.sol"; + +contract MyTokenDiamond is DiamondProxy { + ERC20Facet public erc20Facet; + + function deploy(bytes memory _diamondInit, bytes[] memory _facetCuts) public payable { + // ... deployment logic ... + } + + function setErc20Facet(address _erc20Facet) public onlyOwner { + erc20Facet = ERC20Facet(_erc20Facet); + } + + // Example usage of ERC20Facet functions + function getTokenName() public view returns (string memory) { + return erc20Facet.name(); + } + + function transferTokens(address _to, uint256 _amount) public { + erc20Facet.transfer(_to, _amount); + } +}`} + + +## Best Practices + + +- Initialize the ERC20Facet with the desired token name, symbol, and decimals during diamond deployment. +- Ensure adequate allowance is set before calling `transferFrom` by using the `approve` function. +- Access the facet's storage using the `getStorage` internal function within the facet itself. + + +## Security Considerations + + +This facet adheres to standard ERC-20 security practices. Ensure proper input validation and access control are implemented by any facets that interact with `transfer` or `transferFrom` to prevent vulnerabilities like reentrancy or unauthorized transfers. Use custom errors for clearer revert reasons. The `approve` function requires careful handling to prevent allowance manipulation. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx new file mode 100644 index 00000000..7f9348a0 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx @@ -0,0 +1,422 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "ERC-20 token standard implementation and state management." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token standard implementation and state management. + + + +- Implements standard ERC-20 functions: transfer, approve, transferFrom. +- Supports minting and burning of tokens, updating total supply accordingly. +- Provides a `getStorage` function to access the ERC-20 state within the diamond. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod provides the core logic and storage structure for implementing the ERC-20 token standard within a Compose diamond. It enables standard token operations like transfers, approvals, minting, and burning, while adhering to the diamond's storage pattern for composability and upgradeability. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "@compose/modules/erc20/IERC20Mod.sol"; +import {ERC20Storage } from "@compose/modules/erc20/ERC20Storage.sol"; + +contract MyERC20Facet { + IERC20Mod internal constant ERC20 = IERC20Mod(address(this)); + + function transferToken(address _to, uint256 _amount) external { + address sender = msg.sender; + ERC20.transfer(sender, _to, _amount); + } + + function approveSpender(address _spender, uint256 _amount) external { + address owner = msg.sender; + ERC20.approve(owner, _spender, _amount); + } + + function mintTokens(address _to, uint256 _amount) external { + address minter = msg.sender; + ERC20.mint(minter, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20Storage struct is correctly laid out and initialized in the diamond's deployment. +- Use custom errors for all revert conditions to improve gas efficiency and clarity. +- Always check for the zero address when transferring or approving tokens to prevent unintended behavior. + + +## Integration Notes + + +The ERC20Mod utilizes a dedicated storage slot for its `ERC20Storage` struct. Facets interacting with ERC-20 functionality should import `IERC20Mod` and `ERC20Storage` and use the `getStorage` function or directly reference the storage layout if within the same facet. Ensure no other facets overwrite the ERC-20 storage slot. The `mint` and `burn` functions directly manipulate the total supply and individual balances, impacting all ERC-20 operations. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..67b922d8 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -0,0 +1,443 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "Facilitates cross-chain token transfers for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Facilitates cross-chain token transfers for ERC-20 tokens. + + + +- Authorizes cross-chain minting and burning exclusively to addresses with the `trusted-bridge` role. +- Provides internal checks (`checkTokenBridge`) to validate bridge permissions before executing cross-chain operations. +- Exposes functions to retrieve internal ERC-20 and Access Control storage structures. + + +## Overview + +The ERC20BridgeableFacet enables secure and authorized cross-chain minting and burning of ERC-20 tokens within a Compose diamond. It ensures that only trusted bridge addresses can initiate these operations, maintaining the integrity of token supply across different chains. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "@compose-protocol/diamond/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; +import {DiamondInterface} from "@compose-protocol/diamond/contracts/DiamondInterface.sol"; + +contract ERC20BridgeableConsumer { + address internal diamondAddress; + + // Define selectors for the facet functions + bytes4 internal constant CROSSCHAIN_MINT_SELECTOR = + IERC20BridgeableFacet.crosschainMint.selector; + bytes4 internal constant CROSSCHAIN_BURN_SELECTOR = + IERC20BridgeableFacet.crosschainBurn.selector; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + /** + * @notice Mints ERC20 tokens on a remote chain via the diamond proxy. + * @param _token The address of the ERC20 token. + * @param _to The recipient address on the remote chain. + * @param _amount The amount of tokens to mint. + */ + function mintRemote(address _token, address _to, uint256 _amount) external { + (bool success, ) = diamondAddress.call(abi.encodeWithSelector( + CROSSCHAIN_MINT_SELECTOR, + _token, + _to, + _amount + )); + require(success, "ERC20BridgeableConsumer: crosschain mint failed"); + } + + /** + * @notice Burns ERC20 tokens on a remote chain via the diamond proxy. + * @param _token The address of the ERC20 token. + * @param _from The sender address on the remote chain. + * @param _amount The amount of tokens to burn. + */ + function burnRemote(address _token, address _from, uint256 _amount) external { + (bool success, ) = diamondAddress.call(abi.encodeWithSelector( + CROSSCHAIN_BURN_SELECTOR, + _token, + _from, + _amount + )); + require(success, "ERC20BridgeableConsumer: crosschain burn failed"); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is correctly assigned to authorized cross-chain bridge addresses in the AccessControl facet. +- Verify that the ERC-20 token contract address passed to `crosschainMint` and `crosschainBurn` is valid and accessible. +- Access the facet's storage directly using inline assembly for `getERC20Storage` and `getAccessControlStorage` when querying internal state. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role, preventing unauthorized token supply manipulation. The `checkTokenBridge` internal function ensures that only authorized bridge callers can execute these sensitive operations. Reentrancy is not a concern as these functions do not make external calls to untrusted contracts. Ensure the caller address is not zero before calling `checkTokenBridge` to prevent potential `AccessControlUnauthorizedAccount` errors. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..a8342e0d --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -0,0 +1,420 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "Manages cross-chain ERC20 token bridging operations." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages cross-chain ERC20 token bridging operations. + + + +- Enables cross-chain token minting and burning operations. +- Enforces access control via the `trusted-bridge` role for all cross-chain transfers. +- Provides helper functions to access module-specific storage. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module enables secure cross-chain ERC20 token transfers by allowing trusted bridges to mint and burn tokens. It enforces access control to ensure only authorized entities can perform these critical operations, maintaining the integrity of token balances across different chains. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20BridgeableMod} from "@compose-protocol/diamond-contracts/modules/ERC20BridgeableMod.sol"; +import {DiamondStorage} from "@compose-protocol/diamond-contracts/core/DiamondStorage.sol"; + +contract MyFacet { + using ERC20BridgeableMod for ERC20BridgeableMod.ERC20BridgeableStorage; + using DiamondStorage for DiamondStorage.Storage; + + function exampleCrosschainBurn(address _token, address _to, uint256 _amount) external { + DiamondStorage.Storage storage ds = DiamondStorage.Storage.wrap(address(this)); + ERC20BridgeableMod.ERC20BridgeableStorage storage ebs = ds.erc20BridgeableStorage(); + + // Ensure the caller has the 'trusted-bridge' role before calling + // require(ds.hasRole("trusted-bridge", msg.sender), "Not a trusted bridge"); + + ebs.crosschainBurn(_token, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure that only addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint` functions, typically managed by an AccessControl facet. +- Validate all input parameters for `crosschainBurn` and `crosschainMint` to prevent unexpected behavior or exploits. +- Implement robust logging and event emission for `CrosschainBurn` and `CrosschainMint` to facilitate monitoring and auditing of cross-chain operations. + + +## Integration Notes + + +The `ERC20BridgeableMod` utilizes a dedicated storage slot for its state, accessible via `getERC20Storage`. Functions like `crosschainBurn` and `crosschainMint` operate on this storage. Access control checks for the `trusted-bridge` role are performed internally, relying on the presence and correct configuration of an AccessControl facet. Ensure that the `ERC20BridgeableMod` storage struct is correctly initialized and that the `trusted-bridge` role is properly managed by an AccessControl facet. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx new file mode 100644 index 00000000..97aabf97 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -0,0 +1,339 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "Manages ERC-20 token permits via EIP-2612 signatures." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-20 token permits via EIP-2612 signatures. + + + +- Implements EIP-2612 standard for ERC-20 permits. +- Utilizes domain separators to prevent cross-chain replay attacks. +- Manages nonces to ensure each permit is used only once. + + +## Overview + +The ERC20PermitFacet enables users to grant allowances to token spenders without the spender needing to initiate a transaction. It implements the EIP-2612 standard, allowing for off-chain signing of permit messages, which are then submitted on-chain to set allowances. This facet enhances composability by reducing the need for token holders to interact directly with the token contract for every allowance grant. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit, ERC20PermitFacet} from "@compose-protocol/diamond/contracts/facets/ERC20Permit/ERC20PermitFacet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond/contracts/Diamond.sol"; + +contract Deployer { + address diamondAddress; + + function deployDiamond() external { + // ... deployment logic for diamond and facets ... + // Assume ERC20PermitFacet is deployed and its address is known + address erc20PermitFacetAddress = address(new ERC20PermitFacet()); + + // Add the ERC20PermitFacet to the diamond + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: erc20PermitFacetAddress, + action: IDiamondCut.FacetCutAction.Add, + selectors: + ERC20PermitFacet.getSelectors() // Assuming a helper function to get selectors + }); + // diamond.diamondCut(cut, address(0), ""); // Call diamondCut on your diamond instance + } + + function permitERC20(address tokenAddress, address spender, uint256 value, uint256 deadline, bytes calldata signature) external { + // Assuming the diamond has been cut with ERC20PermitFacet + // Call the permit function through the diamond proxy + // The diamond will route this call to the ERC20PermitFacet + // The actual function call on the diamond will be: + // ERC20PermitFacet(diamondAddress).permit(tokenAddress, spender, value, deadline, signature); + } +}`} + + +## Best Practices + + +- Initialize the `ERC20PermitFacet` with the correct ERC-20 token address during deployment or upgrade. +- Ensure the `deadline` parameter in the `permit` function is set appropriately to prevent stale signatures. +- Verify the signature's validity and the owner's intent before submitting the `permit` transaction. + + +## Security Considerations + + +The `permit` function relies on off-chain signatures. Ensure robust off-chain signing mechanisms are in place to prevent signature malleability or replay attacks. The `deadline` parameter is critical; if set too far in the future, it could allow a compromised key to grant allowances indefinitely until the deadline passes. Input validation for `spender`, `value`, and `deadline` should be handled carefully. The facet should be initialized with the correct ERC-20 token address to prevent granting permits for unintended tokens. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx new file mode 100644 index 00000000..616f9fd5 --- /dev/null +++ b/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -0,0 +1,291 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "ERC-2612 Permit functionality and domain separator" +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-2612 Permit functionality and domain separator + + + +- Implements ERC-2612 permit functionality for gasless approvals. +- Manages and provides the domain separator for signature generation. +- Validates permit signatures to ensure authenticity and prevent replay attacks. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides ERC-2612 permit functionality, enabling gasless approvals for ERC-20 tokens. It manages the domain separator and permit validation, allowing users to grant allowances via signed messages without direct on-chain transactions for the approval itself. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20PermitMod} from "@compose-protocol/diamond-contracts/contracts/modules/ERC20Permit/IERC20PermitMod.sol"; + +contract MyERC20Facet { + IERC20PermitMod internal constant _erc20Permit = IERC20PermitMod(address(0)); // Replace with actual diamond address + + /** + * @notice Allows a user to permit a spender for an allowance via a signed permit. + * @dev This function assumes the caller is a facet that will emit the Approval event. + * @param owner The owner of the tokens. + * @param spender The address permitted to spend tokens. + * @param value The amount of tokens that may be spent. + * @param deadline The deadline for the permit. + * @param v The v component of the signature. + * @param r The r component of the signature. + * @param s The s component of the signature. + */ + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // The permit function in the module validates the signature and updates allowances. + // The calling facet is responsible for emitting the Approval event as per ERC-20 and ERC-2612 standards. + _erc20Permit.permit(owner, spender, value, deadline, v, r, s); + // Emit Approval event here after successful permit validation + // emit Approval(owner, spender, value); // This event emission should be handled by the facet that calls permit + } + + /** + * @notice Retrieves the domain separator for ERC-20 permit signatures. + * @return The domain separator. + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _erc20Permit.DOMAIN_SEPARATOR(); + } +} +`} + + +## Best Practices + + +- Ensure the `Approval` event is emitted by the calling facet after a successful `permit` call to comply with ERC-20/ERC-2612 standards. +- Validate the `deadline` parameter to prevent the use of expired permits. + + +## Integration Notes + + +This module interacts with the diamond's storage to manage ERC-20 allowances and permit data. Facets calling the `permit` function are responsible for emitting the `Approval` event. The `DOMAIN_SEPARATOR` is chain and contract specific, ensuring signature validity only within the context of the deployed diamond. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx new file mode 100644 index 00000000..55d97f54 --- /dev/null +++ b/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -0,0 +1,538 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "Manages ERC-6909 token balances and operator approvals." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-6909 token balances and operator approvals. + + + +- Implements the ERC-6909 token standard for granular asset management. +- Supports standard token transfer and approval functions. +- Includes operator functionality for delegated management of assets. + + +## Overview + +The ERC6909Facet implements the ERC-6909 standard, enabling granular control over token balances and operator permissions within a Compose diamond. It provides essential functions for querying balances, allowances, and operator status, as well as performing token transfers and managing operator relationships. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909} from "@compose/contracts/src/interfaces/IERC6909.sol"; +import {ERC6909Facet} from "@compose/contracts/src/facets/ERC6909Facet.sol"; + +contract MyDiamond is IERC6909 { + // ... other facet interfaces and implementations + + function transfer(address to, uint256 amount) external override returns (bool) { + return ERC6909Facet(this).transfer(to, amount); + } + + function transferFrom(address from, address to, uint256 amount) external override returns (bool) { + return ERC6909Facet(this).transferFrom(from, to, amount); + } + + function approve(address spender, uint256 amount) external override { + ERC6909Facet(this).approve(spender, amount); + } + + function balanceOf(address owner) external view override returns (uint256) { + return ERC6909Facet(this).balanceOf(owner); + } + + function allowance(address owner, address spender) external view override returns (uint256) { + return ERC6909Facet(this).allowance(owner, spender); + } + + function setOperator(address operator, bool approved) external override { + ERC6909Facet(this).setOperator(operator, approved); + } + + function isOperator(address owner, address operator) external view override returns (bool) { + return ERC6909Facet(this).isOperator(owner, operator); + } +}`} + + +## Best Practices + + +- Initialize the ERC6909Facet with appropriate access controls if necessary, though many functions are permissionless. +- When transferring tokens, ensure sufficient balance and allowance checks are performed by the caller or handled by the facet's error reporting. +- Use `setOperator` judiciously to grant or revoke permissions, understanding the implications for operator-driven transactions. + + +## Security Considerations + + +The facet relies on the diamond proxy for access control. Ensure that sensitive operations are appropriately guarded by the diamond's access control mechanism. Standard reentrancy guards are recommended for functions that modify state and can be called externally. Input validation is handled by custom errors to prevent invalid operations. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx new file mode 100644 index 00000000..7f45c057 --- /dev/null +++ b/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -0,0 +1,517 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "Manages ERC-6909 token balances, approvals, and operators." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-6909 token balances, approvals, and operators. + + + +- Manages balances, approvals, and operator roles for multiple token IDs. +- Supports infinite approvals (`type(uint256).max`). +- Operator transfers are bypass allowance checks. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides essential internal functions and storage layout for implementing ERC-6909 minimal multi-token functionality within a Compose diamond. It enables efficient management of token balances, approvals, and operator roles, crucial for composable token standards. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod} from "./IERC6909Mod.sol"; +import {ERC6909Mod} from "./ERC6909Mod.sol"; + +contract MyTokenFacet { + // Assume STORAGE_POSITION is defined elsewhere and points to the ERC6909Mod storage slot + using ERC6909Mod for ERC6909Mod.ERC6909Storage; + + function _transferTokens(address _from, address _to, uint256 _id, uint256 _amount) internal { + ERC6909Mod.ERC6909Storage storage erc6909Storage = ERC6909Mod.getStorage(); + erc6909Storage.transfer(_from, _to, _id, _amount); + } + + function _approveToken(address _spender, uint256 _id, uint256 _amount) internal { + ERC6909Mod.ERC6909Storage storage erc6909Storage = ERC6909Mod.getStorage(); + erc6909Storage.approve(_spender, _id, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `STORAGE_POSITION` for `ERC6909Storage` is correctly defined and immutable. +- Implement access control for functions like `setOperator` if operator registration requires authorization beyond the caller. +- Handle potential `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` errors appropriately in calling facets. + + +## Integration Notes + + +The `ERC6909Mod` requires a dedicated storage slot, defined by `STORAGE_POSITION`, to hold its `ERC6909Storage` struct. Facets using this module must access this storage via the `getStorage()` function. Any facet can read or write to this storage, adhering to the diamond storage pattern. Ensure this storage slot is initialized correctly during deployment and not modified by other facets to maintain data integrity. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx new file mode 100644 index 00000000..91b45d59 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -0,0 +1,209 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "Burn ERC721 tokens within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens within a diamond. + + + +- Destroys ERC721 tokens, adhering to ERC721 standards. +- Emits `Transfer` event with `to` address as address(0) upon successful burn. +- Requires explicit approval or ownership for burning. +- Integrates seamlessly with the Compose Diamond Proxy pattern. + + +## Overview + +The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens managed by a Compose diamond. It ensures that tokens are removed from enumeration tracking and that associated events are emitted, adhering to ERC721 standards. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "@compose/core/facets/erc721/IERC721BurnFacet.sol"; + +contract BurnCaller { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnToken(uint256 tokenId) external { + bytes4 selector = IERC721BurnFacet.burn.selector; + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, tokenId)); + require(success, "Burn failed"); + } +}`} + + +## Best Practices + + +- Ensure the ERC721BurnFacet is properly initialized with the correct storage slot. +- Use the facet's `burn` function to destroy tokens, as it handles necessary state updates and event emissions. +- Verify that the caller has the necessary approvals (via `ERC721.approve` or `ERC721.setApprovalForAll`) before attempting to burn a token they do not own. + + +## Security Considerations + + +The `burn` function requires that the caller is either the owner of the token or has been approved by the owner to burn it. The facet checks for ERC721InsufficientApproval and ERC721NonexistentToken errors. Ensure that approvals are managed correctly to prevent unauthorized burning. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx new file mode 100644 index 00000000..80c730f1 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx @@ -0,0 +1,662 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "Manages ERC-721 token ownership, transfers, and approvals." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-721 token ownership, transfers, and approvals. + + + +- Full ERC-721 (EIP-721) compliance. +- Supports standard token metadata retrieval (`name`, `symbol`, `tokenURI`). +- Enables ownership tracking and transfer logic. +- Manages token approvals for single tokens and for all tokens. + + +## Overview + +The ERC721Facet provides a standard interface for managing non-fungible tokens within a Compose diamond. It handles core ERC-721 functionalities including token ownership, metadata retrieval, and approval mechanisms, enabling seamless integration with other diamond facets. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose/diamond/facets/ERC721/IERC721Facet.sol"; + +contract ERC721Consumer { + IERC721Facet private immutable _erc721Facet; + + constructor(address _erc721FacetAddress) { + _erc721Facet = IERC721Facet(_erc721FacetAddress); + } + + function getTokenName() external view returns (string memory) { + return _erc721Facet.name(); + } + + function getOwner(uint256 tokenId) external view returns (address) { + return _erc721Facet.ownerOf(tokenId); + } + + function safeTransfer(address to, uint256 tokenId) external { + // Ensure caller has approval or is the owner + _erc721Facet.safeTransferFrom(msg.sender, to, tokenId); + } +}`} + + +## Best Practices + + +- Initialize the facet with necessary parameters during diamond deployment. +- Ensure proper access control is implemented for functions like `approve` and `setApprovalForAll` if custom logic is required. +- Be mindful of storage slot conflicts if adding custom state to the ERC721 storage struct. + + +## Security Considerations + + +The `internalTransferFrom` function includes essential checks for ownership and approvals. Users must carefully manage approvals to prevent unauthorized transfers. The `safeTransferFrom` functions include receiver validation to mitigate reentrancy risks associated with non-compliant token receivers. Ensure the diamond proxy's access control layer properly restricts sensitive operations if needed. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx new file mode 100644 index 00000000..2e4b1f81 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx @@ -0,0 +1,358 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "Internal logic for ERC-721 token management." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Internal logic for ERC-721 token management. + + + +- Comprehensive ERC-721 state management functions (mint, burn, transfer). +- Utilizes diamond storage pattern for persistent and composable state. +- Includes explicit error handling for common ERC-721 operations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod module provides essential internal logic for managing ERC-721 tokens within a Compose diamond. It encapsulates core functionalities like minting, burning, and transferring tokens, ensuring consistent and secure state management through the diamond's storage pattern. This allows custom facets to easily integrate robust ERC-721 capabilities without duplicating complex logic. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; + +contract MyERC721Facet { + IERC721Mod public immutable erc721Mod; + + constructor(address _diamondProxy) { + erc721Mod = IERC721Mod(_diamondProxy); + } + + function mintToken(address _to, uint256 _tokenId) external { + erc721Mod.mint(_to, _tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + erc721Mod.transferFrom(_from, _to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + erc721Mod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Mod` facet is initialized and accessible via the diamond proxy. +- Handle specific `ERC721Mod` errors (`ERC721IncorrectOwner`, `ERC721InvalidReceiver`, etc.) in facet logic for robust user feedback. +- Be mindful of token ID uniqueness and the zero address checks enforced by `mint` and `transferFrom`. + + +## Integration Notes + + +The ERC721Mod uses a predefined storage slot to manage its internal ERC721 storage struct. Facets interacting with this module should be aware that all state changes (token ownership, approvals, metadata) are managed internally by ERC721Mod and are reflected across the diamond. The `getStorage` function provides direct access to this internal state, which can be useful for read operations or debugging, but direct modification is not recommended. Ensure the ERC721Mod facet is added to the diamond before any facets that rely on its functionality. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..e95f7bd1 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,225 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "Enables burning of ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enables burning of ERC721 tokens within a Compose diamond. + + + +- Supports the burning of ERC721 tokens. +- Integrates with ERC721Enumerable storage to maintain accurate token counts and ownership lists. +- Emits a `Transfer` event with `from` set to the token owner and `to` set to the zero address upon successful burning. + + +## Overview + +The ERC721EnumerableBurnFacet provides the functionality to burn ERC721 tokens. This facet integrates with the ERC721Enumerable storage pattern, ensuring that burned tokens are correctly removed from the enumeration tracking mechanisms, maintaining the integrity of token ownership and supply. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; +import {ERC721EnumerableBurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/ERC721EnumerableBurnFacet.sol"; + +contract MyDiamond is IERC721EnumerableBurnFacet { + address constant ERC721_ENUMERABLE_BURN_FACET = address(0x...'); // Deployed facet address + + // Other diamond facets and logic + + function burn(uint256 _tokenId) external { + IERC721EnumerableBurnFacet(ERC721_ENUMERABLE_BURN_FACET).burn(_tokenId); + } + + // ... other diamond functions +} + +// To burn a token: +// MyDiamond(diamondAddress).burn(123);`} + + +## Best Practices + + +- Ensure the ERC721EnumerableBurnFacet is correctly initialized with the diamond's ownership and approval logic. +- Verify that the `burn` function is called with a valid token ID that exists and is owned by the caller or approved for transfer. +- When upgrading, ensure the new facet implementation maintains compatibility with existing storage layouts. + + +## Security Considerations + + +The `burn` function requires proper access control to ensure only the token owner or an approved address can burn a token. The facet must correctly handle cases where a token ID does not exist, reverting with `ERC721NonexistentToken`. Ensure sufficient approval checks are in place before burning, reverting with `ERC721InsufficientApproval` if necessary. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..d797fe52 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -0,0 +1,742 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "Enumerable ERC-721 token management and metadata." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enumerable ERC-721 token management and metadata. + + + +- Full ERC-721 compliance with enumeration capabilities. +- Supports standard NFT metadata retrieval via `tokenURI`. +- Includes internal transfer logic for integration with other facets. +- Emits standard ERC-721 events for `Transfer`, `Approval`, and `ApprovalForAll`. + + +## Overview + +The ERC721EnumerableFacet provides standard ERC-721 functionality including token enumeration, ownership tracking, and metadata access. It integrates seamlessly into a Compose diamond, offering a comprehensive surface area for NFT operations. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableFacet} from "@compose-protocol/diamond/facets/ERC721/IERC721EnumerableFacet.sol"; + +contract MyDiamondUser { + IERC721EnumerableFacet immutable erc721Facet; + + constructor(address diamondAddress) { + // Assume diamondAddress is the address of your Compose diamond + // Selector for name() is 0x06395705 + bytes4 nameSelector = 0x06395705; + erc721Facet = IERC721EnumerableFacet(diamondAddress); + } + + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); + } + + function getTotalSupply() external view returns (uint256) { + return erc721Facet.totalSupply(); + } + + function getTokenOwner(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); + } + + function getTokenURI(uint256 tokenId) external view returns (string memory) { + return erc721Facet.tokenURI(tokenId); + } +}`} + + +## Best Practices + + +- Initialize the `ERC721EnumerableFacet` with the correct owner and token details during diamond deployment. +- When transferring tokens, prefer `safeTransferFrom` for enhanced security, especially when interacting with unknown receiver contracts. +- Ensure proper access control is implemented at the diamond proxy level if certain functions require specific permissions. + + +## Security Considerations + + +This facet implements standard ERC-721 logic. Ensure that the diamond proxy enforces appropriate access controls for functions like `approve` and `setApprovalForAll`. The `safeTransferFrom` functions include checks for receiver contract compatibility, mitigating reentrancy risks when transferring to contracts. Validate inputs to prevent errors like `ERC721OutOfBoundsIndex` or `ERC721NonexistentToken`. + + +
+ +
+ + diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..da702238 --- /dev/null +++ b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -0,0 +1,348 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "Manages enumerable ERC721 tokens within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages enumerable ERC721 tokens within a diamond. + + + +- Manages the internal state for enumerable ERC721 tokens. +- Provides `mint`, `burn`, and `transferFrom` functions that update enumeration lists. +- Utilizes inline assembly via `getStorage` to access its state efficiently from the diamond. +- Emits standard ERC721 `Transfer` events when token states change. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721EnumerableMod provides essential internal logic for managing enumerable ERC721 tokens. It ensures tokens are correctly added to and removed from internal tracking lists during minting, burning, and transfers, maintaining a consistent and auditable token supply. This module facilitates the integration of standard ERC721 enumerable behavior into custom diamond facets. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "@compose/modules/erc721/ERC721EnumerableMod.sol"; + +contract MyERC721Facet { + IERC721EnumerableMod internal enumerableMod; + + constructor(address _enumerableModAddress) { + enumerableMod = IERC721EnumerableMod(_enumerableModAddress); + } + + function mintToken(address _to, uint256 _tokenId) external { + // ... other ERC721 logic ... + enumerableMod.mint(_to, _tokenId); + // ... emit Transfer event ... + } + + function burnToken(uint256 _tokenId) external { + // ... get owner and check approvals ... + enumerableMod.burn(_tokenId); + // ... emit Transfer event ... + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + // ... get owner and check approvals ... + enumerableMod.transferFrom(_from, _to, _tokenId); + // ... emit Transfer event ... + } +}`} + + +## Best Practices + + +- Ensure the `ERC721EnumerableMod` contract is correctly initialized with its facet address. +- Always check for and handle the custom errors emitted by the module (e.g., `ERC721NonexistentToken`, `ERC721InvalidReceiver`). +- Integrate module calls within a transaction to maintain atomicity of token state and enumeration updates. + + +## Integration Notes + + +This module interacts with the diamond's storage using a predefined slot for its internal ERC721 enumerable storage struct. Facets integrating this module will call its functions to perform token operations. The module's internal state changes are directly reflected in the diamond's storage, making them visible to all facets that access the relevant storage slots. The order of operations within a facet is critical to ensure the module's state correctly reflects the token's lifecycle. + + +
+ +
+ + diff --git a/website/docs/contracts/token/Royalty/RoyaltyFacet.mdx b/website/docs/contracts/token/Royalty/RoyaltyFacet.mdx new file mode 100644 index 00000000..004c53f7 --- /dev/null +++ b/website/docs/contracts/token/Royalty/RoyaltyFacet.mdx @@ -0,0 +1,193 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "Handles ERC-2981 royalty information." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Handles ERC-2981 royalty information. + + + +- Implements ERC-2981 `royaltyInfo` standard. +- Supports token-specific and default royalty configurations. +- Calculates royalty amounts based on sale price and basis points. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, enabling dApps to query royalty information for NFTs. It provides a standardized way to retrieve royalty details for specific tokens, supporting both token-specific and default royalty configurations. This facet integrates seamlessly into Compose diamonds, enhancing NFT marketplaces and secondary sales. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyFacet} from "@compose-protocol/diamond/contracts/facets/Royalty/IRoyaltyFacet.sol"; + +diamond interface IDiamond { + function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount); +} + +contract MarketplaceExample { + IDiamond immutable diamond; + + constructor(address _diamondAddress) { + diamond = IDiamond(_diamondAddress); + } + + function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { + // Call the royaltyInfo function through the diamond proxy + (receiver, royaltyAmount) = diamond.royaltyInfo(_tokenId, _salePrice); + return (receiver, royaltyAmount); + } +}`} + + +## Best Practices + + +- Ensure the RoyaltyFacet is correctly initialized with default royalty configurations during diamond deployment. +- Access royalty information via the diamond proxy to leverage its routing and access control mechanisms. +- Store royalty-related configurations efficiently, ideally using the provided storage slot to avoid conflicts. + + +## Security Considerations + + +The `royaltyInfo` function is `view` and does not modify state, mitigating reentrancy risks. Ensure that the default royalty receiver and basis points are set appropriately during initialization to prevent unintended royalty distributions. Access to modify royalty settings should be strictly controlled by the diamond's access control layer. + + +
+ +
+ + diff --git a/website/docs/contracts/token/Royalty/RoyaltyMod.mdx b/website/docs/contracts/token/Royalty/RoyaltyMod.mdx new file mode 100644 index 00000000..01215014 --- /dev/null +++ b/website/docs/contracts/token/Royalty/RoyaltyMod.mdx @@ -0,0 +1,356 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "Manages ERC-2981 royalties with default and token-specific settings." +gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-2981 royalties with default and token-specific settings. + + + +- Supports both default and token-specific royalty configurations. +- Implements ERC-2981 standard for royalty payments. +- Provides functions to set, delete, and query royalty information. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides a robust implementation for ERC-2981 royalty standards, enabling diamonds to manage both default and token-specific royalty configurations. It ensures that royalty information is accurately queried, supporting a composable approach to NFT sales and revenue distribution. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "@compose/modules/royalty/IRoyaltyMod.sol"; +import {RoyaltyMod} from "@compose/modules/royalty/RoyaltyMod.sol"; // Example import path + +contract MyNFtFacet { + // Assume IRoyaltyMod is correctly registered with the diamond proxy + IRoyaltyMod internal royaltyMod; + + function initialize(address _diamondAddress) external { + // In a real scenario, this would be set via diamond proxy initialization + royaltyMod = IRoyaltyMod(_diamondAddress); + } + + /** + * @notice Sets royalty for a specific token. + * @param _tokenId The ID of the token. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee in basis points. + */ + function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Gets royalty information for a token and sale price. + * @param _tokenId The ID of the token. + * @param _salePrice The sale price of the token. + * @return receiver The address receiving royalties. + * @return feeAmount The calculated royalty amount. + */ + function getTokenRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 feeAmount) { + (receiver, feeAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); + return (receiver, feeAmount); + } +}`} + + +## Best Practices + + +- Always validate receiver addresses and fee basis points when setting royalties to prevent errors and exploits. +- Utilize `resetTokenRoyalty` to efficiently revert to default royalty settings for a token, simplifying management. +- Be aware that `deleteDefaultRoyalty` will cause `royaltyInfo` to return `(address(0), 0)` for tokens without specific royalty configurations. + + +## Integration Notes + + +The RoyaltyMod stores its state in a dedicated storage slot within the diamond proxy. Facets interact with this module via its interface. Changes to default or token-specific royalties are immediately reflected when calling `royaltyInfo`. The `getStorage` function provides direct access to the module's storage struct, useful for off-chain indexing or advanced logic, but direct manipulation is discouraged in favor of the provided functions. + + +
+ +
+ + From ff6fa8311504bcc485d7920faddca96c4144627c Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 15:11:10 -0500 Subject: [PATCH 41/68] chnage generation to library dir --- .../generate-docs-utils/ai-enhancement.js | 2 - .../generate-docs-utils/category-generator.js | 50 +++++++++---------- .github/scripts/generate-docs-utils/config.js | 4 +- .../doc-generation-utils.js | 4 +- .github/scripts/sync-docs-structure.js | 10 ++-- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/.github/scripts/generate-docs-utils/ai-enhancement.js b/.github/scripts/generate-docs-utils/ai-enhancement.js index b688ec18..687072eb 100644 --- a/.github/scripts/generate-docs-utils/ai-enhancement.js +++ b/.github/scripts/generate-docs-utils/ai-enhancement.js @@ -352,11 +352,9 @@ async function enhanceWithAI(data, contractType, token) { let enhanced; try { enhanced = JSON.parse(responseText); - console.log(' ✅ JSON parsed successfully'); } catch (directParseError) { const cleanedContent = extractJSON(responseText); enhanced = JSON.parse(cleanedContent); - console.log(' ✅ JSON extracted and parsed'); } return convertEnhancedFields(enhanced, data); diff --git a/.github/scripts/generate-docs-utils/category-generator.js b/.github/scripts/generate-docs-utils/category-generator.js index b2cfcd2c..3a310078 100644 --- a/.github/scripts/generate-docs-utils/category-generator.js +++ b/.github/scripts/generate-docs-utils/category-generator.js @@ -270,7 +270,7 @@ function scanSourceStructure() { * Create a _category_.json file for a directory * @param {string} outputDir - Directory to create category file in * @param {string} name - Directory name - * @param {string} relativePath - Relative path from contracts dir + * @param {string} relativePath - Relative path from library dir * @param {number} depth - Nesting depth * @returns {boolean} True if file was created, false if it already existed */ @@ -291,7 +291,7 @@ function createCategoryFile(outputDir, name, relativePath, depth) { label, position, collapsible: true, - collapsed: depth > 1, // Collapse nested categories by default + collapsed: true, // Collapse all categories by default link: { type: 'generated-index', description, @@ -306,30 +306,30 @@ function createCategoryFile(outputDir, name, relativePath, depth) { } /** - * Ensure the base contracts category file exists - * @param {string} contractsDir - Path to contracts directory + * Ensure the base library category file exists + * @param {string} libraryDir - Path to library directory * @returns {boolean} True if created, false if existed */ -function ensureBaseCategory(contractsDir) { - const categoryFile = path.join(contractsDir, '_category_.json'); +function ensureBaseCategory(libraryDir) { + const categoryFile = path.join(libraryDir, '_category_.json'); if (fs.existsSync(categoryFile)) { return false; } const baseCategory = { - label: 'Contracts', + label: 'Library', position: 4, collapsible: true, - collapsed: false, + collapsed: true, // Collapse base Library category by default link: { type: 'generated-index', - title: 'Contract Reference', + title: 'Library Reference', description: 'API reference for all Compose modules and facets.', }, }; - fs.mkdirSync(contractsDir, { recursive: true }); + fs.mkdirSync(libraryDir, { recursive: true }); fs.writeFileSync(categoryFile, JSON.stringify(baseCategory, null, 2) + '\n'); return true; @@ -341,13 +341,13 @@ function ensureBaseCategory(contractsDir) { /** * Compute output path for a source file - * Mirrors the src/ structure in website/docs/contracts/ + * Mirrors the src/ structure in website/docs/library/ * * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/AccessControlMod.sol') * @returns {object} Output path information */ function computeOutputPath(solFilePath) { - const contractsDir = CONFIG.contractsOutputDir || 'website/docs/contracts'; + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; // Normalize path separators const normalizedPath = solFilePath.replace(/\\/g, '/'); @@ -358,7 +358,7 @@ function computeOutputPath(solFilePath) { const parts = relativePath.split('/'); const fileName = parts.pop(); - const outputDir = path.join(contractsDir, ...parts); + const outputDir = path.join(libraryDir, ...parts); const outputFile = path.join(outputDir, `${fileName}.mdx`); return { @@ -380,21 +380,21 @@ function computeOutputPath(solFilePath) { * @param {string} outputDir - Full output directory path */ function ensureCategoryFiles(outputDir) { - const contractsDir = CONFIG.contractsOutputDir || 'website/docs/contracts'; + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; - // Get relative path from contracts base - const relativePath = path.relative(contractsDir, outputDir); + // Get relative path from library base + const relativePath = path.relative(libraryDir, outputDir); if (!relativePath || relativePath.startsWith('..')) { - return; // outputDir is not under contractsDir + return; // outputDir is not under libraryDir } // Ensure base category exists - ensureBaseCategory(contractsDir); + ensureBaseCategory(libraryDir); // Walk up the directory tree, creating category files const parts = relativePath.split(path.sep); - let currentPath = contractsDir; + let currentPath = libraryDir; for (let i = 0; i < parts.length; i++) { currentPath = path.join(currentPath, parts[i]); @@ -417,16 +417,16 @@ function ensureCategoryFiles(outputDir) { */ function syncDocsStructure() { const structure = scanSourceStructure(); - const contractsDir = CONFIG.contractsOutputDir || 'website/docs/contracts'; + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; const created = []; const existing = []; - // Ensure base contracts directory exists with category - if (ensureBaseCategory(contractsDir)) { - created.push('contracts'); + // Ensure base library directory exists with category + if (ensureBaseCategory(libraryDir)) { + created.push('library'); } else { - existing.push('contracts'); + existing.push('library'); } // Create category for each directory in the structure @@ -436,7 +436,7 @@ function syncDocsStructure() { ); for (const [relativePath, info] of sortedPaths) { - const outputDir = path.join(contractsDir, relativePath); + const outputDir = path.join(libraryDir, relativePath); const wasCreated = createCategoryFile( outputDir, info.name, diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index 814b9f88..a8e9327a 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -21,10 +21,10 @@ module.exports = { // ============================================================================ /** - * Base output directory for contract documentation + * Base output directory for library documentation * Structure mirrors src/ automatically */ - contractsOutputDir: 'website/docs/contracts', + libraryOutputDir: 'website/docs/library', // ============================================================================ // Sidebar Positions diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js index c1977977..8d76f7e1 100644 --- a/.github/scripts/generate-docs-utils/doc-generation-utils.js +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -380,10 +380,10 @@ function extractModuleDescriptionFromSource(solFilePath) { function generateDescriptionFromName(contractName) { if (!contractName) return ''; - // Detect contract type from naming convention + // Detect library type from naming convention const isModule = contractName.endsWith('Mod') || contractName.endsWith('Module'); const isFacet = contractName.endsWith('Facet'); - const typeLabel = isModule ? 'module' : isFacet ? 'facet' : 'contract'; + const typeLabel = isModule ? 'module' : isFacet ? 'facet' : 'library'; // Remove suffix and convert CamelCase to readable text const baseName = contractName diff --git a/.github/scripts/sync-docs-structure.js b/.github/scripts/sync-docs-structure.js index d2ff640b..c1bf36b9 100644 --- a/.github/scripts/sync-docs-structure.js +++ b/.github/scripts/sync-docs-structure.js @@ -2,7 +2,7 @@ /** * Sync Documentation Structure * - * Standalone script to mirror the src/ folder structure in website/docs/contracts/ + * Standalone script to mirror the src/ folder structure in website/docs/library/ * Creates _category_.json files for Docusaurus navigation. * * Usage: @@ -46,7 +46,7 @@ function showHelp() { console.log(` Sync Documentation Structure -Mirrors the src/ folder structure in website/docs/contracts/ +Mirrors the src/ folder structure in website/docs/library/ Creates _category_.json files for Docusaurus navigation. Usage: @@ -121,12 +121,12 @@ function displayTree(structure) { function dryRun(structure) { console.log('\n🔍 Dry Run Mode - No changes will be made\n'); - const contractsDir = 'website/docs/contracts'; + const libraryDir = 'website/docs/library'; let wouldCreate = 0; let alreadyExists = 0; // Check base category - const baseCategoryFile = path.join(contractsDir, '_category_.json'); + const baseCategoryFile = path.join(libraryDir, '_category_.json'); if (fs.existsSync(baseCategoryFile)) { console.log(` ✓ ${baseCategoryFile} (exists)`); alreadyExists++; @@ -137,7 +137,7 @@ function dryRun(structure) { // Check each category for (const [relativePath] of structure) { - const categoryFile = path.join(contractsDir, relativePath, '_category_.json'); + const categoryFile = path.join(libraryDir, relativePath, '_category_.json'); if (fs.existsSync(categoryFile)) { if (options.verbose) { console.log(` ✓ ${categoryFile} (exists)`); From c291a2b38d639e6157ab62e7732ed7a22ca2111a Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sun, 21 Dec 2025 20:16:46 +0000 Subject: [PATCH 42/68] docs: auto-generate docs pages from NatSpec --- website/docs/library/_category_.json | 11 + .../AccessControl/AccessControlFacet.mdx | 566 +++++++++++++ .../access/AccessControl/AccessControlMod.mdx | 443 +++++++++++ .../access/AccessControl/_category_.json | 10 + .../AccessControlPausableFacet.mdx | 373 +++++++++ .../AccessControlPausableMod.mdx | 381 +++++++++ .../AccessControlPausable/_category_.json | 10 + .../AccessControlTemporalFacet.mdx | 446 +++++++++++ .../AccessControlTemporalMod.mdx | 479 +++++++++++ .../AccessControlTemporal/_category_.json | 10 + .../docs/library/access/Owner/OwnerFacet.mdx | 209 +++++ .../docs/library/access/Owner/OwnerMod.mdx | 253 ++++++ .../docs/library/access/Owner/_category_.json | 10 + .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 292 +++++++ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 296 +++++++ .../access/OwnerTwoSteps/_category_.json | 10 + website/docs/library/access/_category_.json | 10 + .../docs/library/diamond/DiamondCutFacet.mdx | 419 ++++++++++ .../docs/library/diamond/DiamondCutMod.mdx | 393 +++++++++ .../library/diamond/DiamondLoupeFacet.mdx | 250 ++++++ website/docs/library/diamond/DiamondMod.mdx | 238 ++++++ website/docs/library/diamond/_category_.json | 10 + .../diamond/example/ExampleDiamond.mdx | 150 ++++ .../library/diamond/example/_category_.json | 10 + .../interfaceDetection/ERC165/ERC165Mod.mdx | 151 ++++ .../interfaceDetection/ERC165/_category_.json | 10 + .../interfaceDetection/_category_.json | 10 + .../library/libraries/NonReentrancyMod.mdx | 141 ++++ .../docs/library/libraries/_category_.json | 10 + .../library/token/ERC1155/ERC1155Facet.mdx | 666 ++++++++++++++++ .../docs/library/token/ERC1155/ERC1155Mod.mdx | 607 ++++++++++++++ .../library/token/ERC1155/_category_.json | 10 + .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 247 ++++++ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 579 ++++++++++++++ .../library/token/ERC20/ERC20/ERC20Mod.mdx | 418 ++++++++++ .../library/token/ERC20/ERC20/_category_.json | 10 + .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 417 ++++++++++ .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 419 ++++++++++ .../ERC20/ERC20Bridgeable/_category_.json | 10 + .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 324 ++++++++ .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 276 +++++++ .../token/ERC20/ERC20Permit/_category_.json | 10 + .../docs/library/token/ERC20/_category_.json | 10 + .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 529 +++++++++++++ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 522 ++++++++++++ .../token/ERC6909/ERC6909/_category_.json | 10 + .../library/token/ERC6909/_category_.json | 10 + .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 218 +++++ .../token/ERC721/ERC721/ERC721Facet.mdx | 666 ++++++++++++++++ .../library/token/ERC721/ERC721/ERC721Mod.mdx | 356 +++++++++ .../token/ERC721/ERC721/_category_.json | 10 + .../ERC721EnumerableBurnFacet.mdx | 226 ++++++ .../ERC721EnumerableFacet.mdx | 748 ++++++++++++++++++ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 348 ++++++++ .../ERC721/ERC721Enumerable/_category_.json | 10 + .../docs/library/token/ERC721/_category_.json | 10 + .../library/token/Royalty/RoyaltyFacet.mdx | 189 +++++ .../docs/library/token/Royalty/RoyaltyMod.mdx | 365 +++++++++ .../library/token/Royalty/_category_.json | 10 + website/docs/library/token/_category_.json | 10 + 60 files changed, 13841 insertions(+) create mode 100644 website/docs/library/_category_.json create mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx create mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx create mode 100644 website/docs/library/access/AccessControl/_category_.json create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx create mode 100644 website/docs/library/access/AccessControlPausable/_category_.json create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json create mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx create mode 100644 website/docs/library/access/Owner/OwnerMod.mdx create mode 100644 website/docs/library/access/Owner/_category_.json create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json create mode 100644 website/docs/library/access/_category_.json create mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx create mode 100644 website/docs/library/diamond/DiamondCutMod.mdx create mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx create mode 100644 website/docs/library/diamond/DiamondMod.mdx create mode 100644 website/docs/library/diamond/_category_.json create mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx create mode 100644 website/docs/library/diamond/example/_category_.json create mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json create mode 100644 website/docs/library/interfaceDetection/_category_.json create mode 100644 website/docs/library/libraries/NonReentrancyMod.mdx create mode 100644 website/docs/library/libraries/_category_.json create mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx create mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx create mode 100644 website/docs/library/token/ERC1155/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json create mode 100644 website/docs/library/token/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json create mode 100644 website/docs/library/token/ERC721/_category_.json create mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx create mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx create mode 100644 website/docs/library/token/Royalty/_category_.json create mode 100644 website/docs/library/token/_category_.json diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json new file mode 100644 index 00000000..4af407fc --- /dev/null +++ b/website/docs/library/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Library", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "title": "Library Reference", + "description": "API reference for all Compose modules and facets." + } +} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx new file mode 100644 index 00000000..2c5d71f4 --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -0,0 +1,566 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Manages roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages roles and permissions within a diamond. + + + +- Role-Based Access Control (RBAC) system. +- Supports granting and revoking roles to individual accounts or batches. +- Administered roles can be managed by designated admin roles. +- Provides functions to check and enforce role ownership. + + +## Overview + +The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular management of permissions by defining roles and assigning them to accounts. This facet enables administrators to grant, revoke, and renounce roles, ensuring that only authorized entities can perform specific actions. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlFacet} from "@compose/diamond/facets/AccessControl/IAccessControlFacet.sol"; +import {IDiamondCut} from "@compose/diamond/facets/DiamondCut/IDiamondCut.sol"; + +// Assume diamondCutFacet is the selector for the DiamondCutFacet +// Assume accessControlFacet is the selector for the AccessControlFacet + +contract Deployer { + address public diamondAddress; + + function deployDiamond(address _diamondAdmin) public { + // Deployment logic for the diamond proxy... + diamondAddress = address(0); // Placeholder for actual diamond address + } + + function grantAdminRole() public { + IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); + address _diamondAdmin = msg.sender; // Or any other admin address + bytes32 adminRole = keccak256("ADMIN_ROLE"); + + accessControl.grantRole(adminRole, _diamondAdmin); + } + + function revokeRoleFromUser(address _user) public { + IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); + bytes32 userRole = keccak256("USER_ROLE"); + + accessControl.revokeRole(userRole, _user); + } + + function hasUserRole(address _user) public view returns (bool) { + IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); + bytes32 userRole = keccak256("USER_ROLE"); + + return accessControl.hasRole(userRole, _user); + } + + function requireUserRole(address _user) public view { + IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); + bytes32 userRole = keccak256("USER_ROLE"); + + accessControl.requireRole(userRole, _user); + } +}`} + + +## Best Practices + + +- Grant roles only to trusted addresses. The role of the entity granting roles should itself be strictly controlled. +- Use `grantRoleBatch` and `revokeRoleBatch` for efficiency when managing multiple accounts for the same role. +- Define roles using `bytes32` hashes for clarity and to prevent accidental role collisions. + + +## Security Considerations + + +Ensure that the caller has the necessary administrative privileges before granting or revoking roles. Reentrancy is not a concern for role management functions as they do not make external calls. Input validation is handled by the underlying diamond proxy logic; however, ensure correct role identifiers are used. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx new file mode 100644 index 00000000..e46e66bc --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -0,0 +1,443 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Manage roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within a diamond. + + + +- Role-based access control for diamond functions. +- Permission management for granting and revoking roles. +- Ability to set administrative roles for other roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides a robust Access Control system, enabling granular management of roles and permissions for accounts interacting with the diamond. It ensures that sensitive operations are restricted to authorized entities, enhancing the security and integrity of the diamond's functionalities. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose/modules/access-control/IAccessControl.sol"; + +contract MyFacet { + IAccessControl immutable accessControl; + + constructor(address _accessControlAddress) { + accessControl = IAccessControl(_accessControlAddress); + } + + function grantAdminRole(address _account) external { + // Ensure the caller has the ADMIN_ROLE before granting it + accessControl.requireRole(IAccessControl.ADMIN_ROLE, msg.sender); + accessControl.grantRole(IAccessControl.ADMIN_ROLE, _account); + } + + function checkAdmin(address _account) external view returns (bool) { + return accessControl.hasRole(IAccessControl.ADMIN_ROLE, _account); + } +}`} + + +## Best Practices + + +- Use custom errors for role-based reverts for gas efficiency and clarity. +- Ensure that only authorized roles can manage other roles (e.g., ADMIN_ROLE manages all other roles). +- When revoking roles, always verify the caller's permissions. + + +## Integration Notes + + +The AccessControl module relies on its own storage slot within the diamond. Facets interact with this module by calling its external functions through the diamond proxy. Changes to role assignments are immediately visible to all facets upon the transaction's successful execution. The `AccessControlMod` stores role assignments and admin role configurations. When implementing new facets that require access control, call `hasRole` or `requireRole` to enforce permissions. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json new file mode 100644 index 00000000..25db9246 --- /dev/null +++ b/website/docs/library/access/AccessControl/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Role-based access control (RBAC) pattern." + } +} diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..9a4e3353 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -0,0 +1,373 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Manage role pausing and access control within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage role pausing and access control within a diamond. + + + +- Temporarily disable specific roles using `pauseRole`. +- Re-enable paused roles with `unpauseRole`. +- Check role pause status via `isRolePaused`. +- Integrates seamlessly with the diamond's existing access control mechanisms. + + +## Overview + +This facet provides granular control over role permissions by allowing specific roles to be temporarily paused. It integrates with the diamond's access control system, ensuring that paused roles cannot be invoked. This enables robust operational management and emergency halt capabilities for critical functions. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose-protocol/diamond-contracts/facets/DiamondLoupe.sol"; +import {AccessControlPausableFacet} from "@compose-protocol/diamond-contracts/facets/AccessControlPausable.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/interfaces/IDiamondCut.sol"; + +contract Deployer { + address immutable DIAMOND_ADDRESS; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function pauseRole(bytes4 roleSelector) external { + AccessControlPausableFacet(DIAMOND_ADDRESS).pauseRole(roleSelector); + } + + function unpauseRole(bytes4 roleSelector) external { + AccessControlPausableFacet(DIAMOND_ADDRESS).unpauseRole(roleSelector); + } + + function isRolePaused(bytes4 roleSelector) external view returns (bool) { + return AccessControlPausableFacet(DIAMOND_ADDRESS).isRolePaused(roleSelector); + } +}`} + + +## Best Practices + + +- Initialize roles and grant permissions before pausing or unpausing them. +- Use `pauseRole` for temporary operational halts and `unpauseRole` to resume functionality. +- Ensure the caller has the appropriate administrative role to pause or unpause a specific role. + + +## Security Considerations + + +Access to `pauseRole` and `unpauseRole` is restricted to the admin of the specific role, preventing unauthorized pausing or unpausing. The `requireRoleNotPaused` internal function ensures that calls to paused roles are reverted, preventing unintended execution. Ensure that role administration is correctly configured to maintain security. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx new file mode 100644 index 00000000..94cac87a --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -0,0 +1,381 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Manages role-based access control with pause functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages role-based access control with pause functionality. + + + +- Role-based access control enforcement. +- Ability to pause and unpause specific roles, halting associated operations. +- Reverts with specific errors for unauthorized access or paused roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module integrates role-based access control with the ability to pause specific roles. It allows for granular control over who can perform actions and provides a mechanism to temporarily halt operations for a role during critical events or maintenance. This ensures operational safety and administrative flexibility within a Compose diamond. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableMod} from "@compose/diamond/modules/access-control/pausable/IAccessControlPausableMod.sol"; + +contract MyFacet { + IAccessControlPausableMod internal accessControlPausableMod; + + constructor(address _accessControlPausableMod) { + accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); + } + + function someProtectedFunction() external { + // Assuming 'MY_ROLE' is a defined role ID + address caller = msg.sender; + uint256 roleId = 1; // Example Role ID + accessControlPausableMod.requireRoleNotPaused(caller, roleId); + + // Proceed with function logic + } + + function pauseMyRole() external { + uint256 roleId = 1; // Example Role ID + accessControlPausableMod.pauseRole(roleId); + } + + function unpauseMyRole() external { + uint256 roleId = 1; // Example Role ID + accessControlPausableMod.unpauseRole(roleId); + } +}`} + + +## Best Practices + + +- Use `requireRoleNotPaused` to enforce both role membership and operational status before executing sensitive logic. +- Implement separate administrative functions to call `pauseRole` and `unpauseRole`, restricting access to authorized roles or the owner. +- Handle `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` errors explicitly in calling facets to provide clear user feedback. + + +## Integration Notes + + +This module manages its state within its own storage slots. Facets interact with it via its interface. The `requireRoleNotPaused` function checks both role presence and the pause status of that role. Changes to role pause states are immediately visible to all facets interacting with the module. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json new file mode 100644 index 00000000..ab207a3c --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Pausable Access Control", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "RBAC with pause functionality." + } +} diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..8e829b4c --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -0,0 +1,446 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Manages time-bound role assignments in Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages time-bound role assignments in Compose diamonds. + + + +- Grants roles with specific expiry timestamps. +- Provides a mechanism to check if a role assignment has expired. +- Enforces role validity before execution of sensitive operations. + + +## Overview + +The AccessControlTemporalFacet extends Compose's access control system by introducing time-bound role assignments. This facet allows administrators to grant roles that automatically expire, enhancing granular control over permissions within a diamond. It provides functions to manage these temporal roles and check their validity. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IComposeDiamond} from "@compose/diamond/contracts/interfaces/IComposeDiamond.sol"; +import {AccessControlTemporalFacet} from "@compose/access-control/contracts/AccessControlTemporalFacet.sol"; + +contract DeployExample { + IComposeDiamond immutable diamondProxy; + + constructor(address _diamondProxy) { + diamondProxy = IComposeDiamond(_diamondProxy); + } + + function grantRoleWithExpiry(bytes32 _role, address _account, uint64 _expiry) external { + address facetAddress = diamondProxy.facetAddress(AccessControlTemporalFacet.FACET_FUNCTION_SELECTORS.grantRoleWithExpiry); + AccessControlTemporalFacet(facetAddress).grantRoleWithExpiry(_role, _account, _expiry); + } + + function isRoleExpired(bytes32 _role, address _account) external view returns (bool) { + address facetAddress = diamondProxy.facetAddress(AccessControlTemporalFacet.FACET_FUNCTION_SELECTORS.isRoleExpired); + return AccessControlTemporalFacet(facetAddress).isRoleExpired(_role, _account); + } +}`} + + +## Best Practices + + +- Grant temporal roles only to trusted administrators who understand the expiry implications. +- Regularly audit expiring roles to ensure continued necessity or timely renewal. +- Use `requireValidRole` in critical functions to enforce active role assignments. + + +## Security Considerations + + +Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role management. The `requireValidRole` function prevents the use of expired roles by reverting with `AccessControlRoleExpired`. Ensure the `admin` role itself is securely managed. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..03f3ca2c --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -0,0 +1,479 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Manages role assignments with time-based expiry." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages role assignments with time-based expiry. + + + +- Grants roles with a configurable expiry timestamp. +- Automatically checks for role expiry, preventing access to expired roles. +- Provides explicit functions to check role validity and revoke temporal roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module extends role-based access control by introducing time-bound role assignments. It allows for roles to be granted with a specific expiry timestamp, enhancing security and operational flexibility. By managing temporal roles, diamonds can enforce time-sensitive permissions, automating the revocation of access when it's no longer needed. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {AccessControlTemporalMod} from "@compose/contracts/modules/AccessControlTemporalMod.sol"; +import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; + +contract MyFacet { + AccessControlTemporalMod internal acTemporal; + + function initialize(address diamondAdmin) { + // Assume AccessControlTemporalMod facet is deployed and its address is known + address acTemporalAddress = address(0x123); // Replace with actual facet address + acTemporal = AccessControlTemporalMod(acTemporalAddress); + } + + function grantAdminRoleWithExpiry(address user, uint64 expiry) external { + // Assuming the facet has access to the diamond's admin role + bytes32 adminRole = keccak256("DIAMOND_ADMIN_ROLE"); + acTemporal.grantRoleWithExpiry(adminRole, user, expiry); + } + + function checkAdminStatus(address user) external view { + bytes32 adminRole = keccak256("DIAMOND_ADMIN_ROLE"); + acTemporal.requireValidRole(adminRole, user); + } +}`} + + +## Best Practices + + +- Use `requireValidRole` in external functions to enforce time-bound access control before executing sensitive operations. +- Ensure the `expiry` timestamp passed to `grantRoleWithExpiry` is in Unix time (seconds since epoch). +- Regularly audit temporal role assignments and consider automated mechanisms for role cleanup. + + +## Integration Notes + + +This module utilizes the standard Compose diamond storage pattern. Facets interacting with this module will need to access its storage via the diamond proxy. The `AccessControlTemporalMod` facet manages its own internal storage for role expiry information, which is distinct from the core Access Control storage. Any facet that needs to check or manage temporal roles must be aware of this module's presence and call its functions directly. The `requireValidRole` function is designed to be called by other facets to enforce access control checks, reverting with specific errors if the role is unauthorized or expired. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json new file mode 100644 index 00000000..72012bb3 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Temporal Access Control", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Time-limited role-based access control." + } +} diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx new file mode 100644 index 00000000..688cdec1 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -0,0 +1,209 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Manages diamond ownership and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond ownership and transfers. + + + +- Permissioned ownership control for administrative functions. +- Supports safe transfer of ownership to prevent accidental loss of control. +- Allows for complete renouncement of ownership. + + +## Overview + +The OwnerFacet provides essential functionality for managing the ownership of a Compose diamond. It allows for querying the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative actions within the diamond. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; + +contract OwnerCaller { + IOwnerFacet private immutable _ownerFacet; + + constructor(address _diamondAddress) { + _ownerFacet = IOwnerFacet(_diamondAddress); + } + + function callOwner() external { + address currentOwner = _ownerOwner(); + // Example: Transfer ownership if allowed + // _ownerFacet.transferOwnership(newOwner); + } + + function _ownerOwner() internal view returns (address) { + return _ownerFacet.owner(); + } +}`} + + +## Best Practices + + +- Ensure the OwnerFacet is initialized with the correct owner address during diamond deployment. +- Use `transferOwnership` to delegate administrative control safely. +- Consider the implications of `renounceOwnership` as it makes the diamond unmanageable by any single address. + + +## Security Considerations + + +Access control is paramount. Only the current owner can execute `transferOwnership` and `renounceOwnership`. Ensure that the address performing these actions is indeed the rightful owner to prevent unauthorized control of the diamond. Input validation is handled by the `transferOwnership` function, which reverts if `_newOwner` is the zero address, except when explicitly intended for renouncement. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx new file mode 100644 index 00000000..925a57ab --- /dev/null +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -0,0 +1,253 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "Manages ERC-173 contract ownership." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-173 contract ownership. + + + +- Implements ERC-173 ownership standard. +- Provides `owner()` view function to retrieve the current owner's address. +- Includes `requireOwner()` for easy access control enforcement. +- Supports renouncing ownership by setting the new owner to `address(0)`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerMod module provides essential functionality for managing contract ownership according to the ERC-173 standard. It ensures that only the designated owner can perform critical administrative actions, enhancing security and control within the diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "./interfaces/IOwnerMod.sol"; + +contract MyOwnerFacet { + // Assumes OwnerMod is deployed at the correct STORAGE_POSITION + IOwnerMod public ownerMod; + + function transferMyContractOwnership(address _newOwner) external { + // Call the transferOwnership function from the OwnerMod + ownerMod.transferOwnership(_newOwner); + } + + function getMyContractOwner() external view returns (address) { + // Call the owner function from the OwnerMod + return ownerMod.owner(); + } + + function onlyOwnerAction() internal view { + // Use the requireOwner check from OwnerMod + ownerMod.requireOwner(); + // ... owner-only logic ... + } +}`} + + +## Best Practices + + +- Grant ownership only to trusted addresses. Use `transferOwnership` carefully, especially when renouncing ownership. +- Implement `requireOwner` checks before critical administrative functions to enforce access control. +- Be aware that changing ownership is a state-altering operation and should be handled with care during upgrades. + + +## Integration Notes + + +The OwnerMod stores ownership data in a dedicated storage slot, typically defined by `STORAGE_POSITION`. Facets that depend on ownership checks or need to manage ownership should import the `IOwnerMod` interface and interact with the module's functions. The `getStorage` function can be used to retrieve a pointer to the internal storage struct, allowing for direct access if necessary, though calling the provided functions is recommended for maintaining module integrity. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json new file mode 100644 index 00000000..274b507b --- /dev/null +++ b/website/docs/library/access/Owner/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Owner", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Single-owner access control pattern." + } +} diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..5438a32d --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -0,0 +1,292 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Manage diamond ownership with a two-step transfer process." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond ownership with a two-step transfer process. + + + +- Two-step ownership transfer prevents accidental changes. +- Provides functions to query current and pending ownership. +- Supports `renounceOwnership` for relinquishing control. + + +## Overview + +This facet provides a robust, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are intentional and secure by requiring both the current owner to initiate a transfer and the new owner to accept it, preventing accidental or unauthorized takeovers. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "@compose/facets/owner/IOwnerTwoStepsFacet.sol"; + +contract DiamondOwnerManager { + IOwnerTwoStepsFacet public ownerFacet; + + constructor(address _ownerFacetAddress) { + ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); + } + + function initiateOwnershipTransfer(address _newOwner) external { + // Assuming the caller is the current owner + ownerFacet.transferOwnership(_newOwner); + } + + function acceptNewOwnership() external { + // Assuming the caller is the pending owner + ownerFacet.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function getPendingOwner() external view returns (address) { + return ownerFacet.pendingOwner(); + } +}`} + + +## Best Practices + + +- Initialize the facet with the current owner's address during diamond deployment. +- Ensure that only the current owner can call `transferOwnership` and only the pending owner can call `acceptOwnership`. +- Use `owner()` and `pendingOwner()` to monitor ownership status. + + +## Security Considerations + + +Access control is critical: `transferOwnership` must be callable only by the current owner, and `acceptOwnership` only by the pending owner. Failure to enforce this can lead to unauthorized ownership changes. The facet itself does not manage reentrancy; dependent facets or the diamond proxy must handle this. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..6b33932c --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -0,0 +1,296 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "Two-step ownership transfer for secure contract management." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Two-step ownership transfer for secure contract management. + + + +- Secure two-step ownership transfer to prevent accidental loss. +- Explicit `transferOwnership` and `acceptOwnership` functions. +- `renounceOwnership` function to relinquish control. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module implements a secure two-step ownership transfer mechanism, preventing accidental ownership loss. It ensures that ownership changes are confirmed by both the current and pending owners, enhancing the safety of critical contract administration functions. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoSteps} from "./interfaces/IOwnerTwoSteps.sol"; + +contract MyOwnerFacet { + IOwnerTwoSteps private immutable _ownerTwoSteps; + + constructor(address ownerTwoStepsAddress) { + _ownerTwoSteps = IOwnerTwoSteps(ownerTwoStepsAddress); + } + + function transferContractOwnership(address _newOwner) external { + _ownerTwoSteps.transferOwnership(_newOwner); + } + + function acceptContractOwnership() external { + _ownerTwoSteps.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return _ownerTwoSteps.owner(); + } +}`} + + +## Best Practices + + +- Use `transferOwnership` only when initiating a change. The recipient must then call `acceptOwnership`. +- Call `renounceOwnership` with extreme caution, as it renders all owner-restricted functions unusable. +- Always check the return value of `owner()` and `pendingOwner()` to understand the current and intended ownership status. + + +## Integration Notes + + +The `OwnerTwoStepsMod` module manages ownership state in dedicated storage slots. Facets integrating this module should reference these slots using inline assembly as demonstrated in `getOwnerStorage` and `getPendingOwnerStorage`. The module enforces ownership checks via the `requireOwner` function, which must be called by any facet function requiring owner privileges. Ensure the `OwnerTwoStepsMod` facet is added to the diamond before any other facets that rely on its ownership management. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json new file mode 100644 index 00000000..52fea0d0 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Two-Step Owner", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Two-step ownership transfer pattern." + } +} diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json new file mode 100644 index 00000000..3d29d95a --- /dev/null +++ b/website/docs/library/access/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Access control patterns for permission management in Compose diamonds." + } +} diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx new file mode 100644 index 00000000..3faae578 --- /dev/null +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -0,0 +1,419 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Manage diamond facets and functions" +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and functions + + + +- Allows adding, replacing, and removing facets and their functions. +- Supports executing an initialization function call after a diamond cut. +- Provides functions to inspect diamond storage and facet ownership. + + +## Overview + +The DiamondCutFacet provides essential functions for managing the diamond proxy's facets and their associated functions. It allows authorized users to add, replace, or remove facets and functions, enabling dynamic upgrades and modifications of the diamond's functionality. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet} from "@compose/diamond-contracts/facets/DiamondCutFacet.sol"; +import {IDiamondCut} from "@compose/diamond-contracts/interfaces/IDiamondCut.sol"; + +contract MyDiamond is IDiamondCut { + // ... other facets and storage + + function upgradeDiamond() external { + address diamondCutFacetAddress = address(this); // Assume deployed and accessible + bytes32[] memory selectors = new bytes32[](1); + selectors[0] = IDiamondCut.diamondCut.selector; + + // Example: Adding a new facet + // diamondCut(newFacetCuts, initialFacets, initAddress, initCalldata) + diamondCut( + new IDiamondCut.FacetCut[](0), // No facets to add initially for this example + new IDiamondCut.FacetCut[](1) {{ + facetAddress = address(new NewFacet()); // Address of the new facet contract + action = IDiamondCut.FacetCutAction.ADD; + isPure = false; // or true if the facet only contains view functions + functionSelectors = selectors; // Selectors to be added + }}, + address(0), // No initialization contract + '()' // Empty initialization calldata + ); + } + + // ... +}`} + + +## Best Practices + + +- Only the diamond owner or an authorized address should call `diamondCut` functions. +- Ensure facet addresses are verified before adding or replacing to prevent malicious code injection. +- Carefully manage function selector mappings to avoid conflicts and ensure correct dispatch. + + +## Security Considerations + + +The `diamondCut` function is highly sensitive and must be protected by robust access control. Incorrectly adding or replacing facets can lead to unexpected behavior or rug pulls. Ensure all facet code is audited before deployment. The `removeFunctions` and `removeFacet` actions require careful consideration to avoid bricking the diamond. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx new file mode 100644 index 00000000..94479b4e --- /dev/null +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -0,0 +1,393 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Manages diamond facet additions, replacements, and removals." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond facet additions, replacements, and removals. + + + +- Atomic facet updates: Add, replace, or remove multiple facets in a single transaction. +- Optional initialization: Supports executing an initialization function during the cut process. +- Immutable function protection: Prevents accidental removal or replacement of critical immutable functions. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCutMod provides the core functionality for managing facets within a Compose diamond. It enables atomic updates to the diamond's interface by adding, replacing, or removing functions. This module is crucial for upgrading and extending diamond capabilities in a controlled and gas-efficient manner. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-proxy/contracts/modules/diamondCut/IDiamondCut.sol"; +import {DiamondCutMod} from "@compose/diamond-proxy/contracts/modules/diamondCut/DiamondCutMod.sol"; + +contract MyDiamond is IDiamondCut { + address constant DIAMOND_CUT_MODULE = address(new DiamondCutMod()); + + function diamondCut(FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) external override { + // Delegate to the DiamondCutMod to perform the cut + IDiamondCut(DIAMOND_CUT_MODULE).diamondCut(_diamondCut, _init, _calldata); + } + + // Other diamond functions and selectors... +} + +// Example of calling diamondCut from another facet: +contract FacetManager { + IDiamondCut public diamondProxy; + + constructor(address _diamondProxy) { + diamondProxy = IDiamondCut(_diamondProxy); + } + + function updateFacet(bytes4[] memory _selectors, address _newFacetAddress, address _facetAddressToReplace) public { + // Assuming DiamondCutMod is deployed at a known address or accessible via the diamond proxy itself + // For simplicity, assume diamondProxy is the interface to the diamond's cut functionality + FacetCut[] memory cuts = new FacetCut[](1); + cuts[0] = FacetCut({ + facetAddress: _newFacetAddress, + action: 2, // REPLACE + selectors: _selectors + }); + + // In a real scenario, you'd need to know the correct init address and calldata if applicable + diamondProxy.diamondCut(cuts, address(0), ""); + } +}`} + + +## Best Practices + + +- Use `diamondCut` for all facet modifications to ensure atomicity and proper state transitions. +- Carefully review selectors and facet addresses before execution to prevent unintended function loss or overwrites. +- Handle errors such as `CannotAddFunctionToDiamondThatAlreadyExists` and `CannotRemoveImmutableFunction` to ensure robust upgrade logic. + + +## Integration Notes + + +The DiamondCutMod stores facet information and selectors within the diamond's storage. It uses a mapping of selectors to facet addresses. Any changes made via `diamondCut` are immediately reflected in the diamond proxy's dispatch mechanism. Facets that interact with functions managed by DiamondCutMod should be aware that function addresses can change during upgrades. The order of operations within a single `diamondCut` call matters for replacements and additions. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..b52f59f9 --- /dev/null +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -0,0 +1,250 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "Inspect diamond facets, selectors, and storage." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Inspect diamond facets, selectors, and storage. + + + +- Provides detailed introspection of diamond facets and their associated function selectors. +- Offers efficient querying of facet addresses and function mappings using optimized internal logic. +- Enables retrieval of all deployed facets and their selectors in a single call. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, the functions they implement, and the addresses they are mapped to. This facet is crucial for understanding the diamond's surface area and for debugging. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupe} from "./IDiamondLoupe.sol"; + +contract MyDiamond { + IDiamondLoupe constant diamondLoupe = IDiamondLoupe(address(this)); + + function getFacetAddresses() external view returns (address[] memory) { + return diamondLoupe.facetAddresses(); + } + + function getFacetSelectors(address _facet) external view returns (bytes4[] memory) { + return diamondLoupe.facetFunctionSelectors(_facet); + } + + function getAllFacets() external view returns (Facet[] memory) { + return diamondLoupe.facets(); + } +}`} + + +## Best Practices + + +- Deploy this facet as part of the initial diamond setup or upgrade process. +- Use the functions provided for debugging and understanding the diamond's composition, rather than for core application logic. +- Ensure the diamond's internal storage (`DiamondStorage`) is accessible to this facet for accurate data retrieval. + + +## Security Considerations + + +This facet is read-only and does not modify state, making it inherently safe from reentrancy attacks. Access control is typically managed at the diamond proxy level; ensure that access to these introspection functions is appropriately restricted if sensitive information about the diamond's structure needs to be protected. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx new file mode 100644 index 00000000..83101a6a --- /dev/null +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -0,0 +1,238 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Internal functions and storage for diamond proxy." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Internal functions and storage for diamond proxy. + + + +- Manages facet registration and function selector mapping during diamond deployment. +- Provides the core fallback mechanism for routing external calls to registered facets. +- Exposes diamond's internal storage for inspection. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondMod module manages facet additions and provides core proxy logic for function dispatch. It ensures that facets are correctly registered and that function calls are routed to the appropriate facet implementation, forming the bedrock of diamond composability. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import { IDiamondMod } from "@compose/diamond-proxy/src/modules/diamondMod/IDiamondMod.sol"; + +contract MyFacet { + IDiamondMod public diamondMod; + + function initialize(IDiamondMod _diamondMod) external { + diamondMod = _diamondMod; + } + + function addMyFacet() external { + // This function would typically be called during diamond deployment. + // For demonstration, assuming diamondMod is already initialized. + // In a real scenario, this logic resides in the diamond deployer. + // diamondMod.addFacets(...); // Not directly callable by external facets post-deployment. + } + + function getDiamondStorage() external view returns (bytes memory) { + return diamondMod.getStorage(); + } +}`} + + +## Best Practices + + +- Facet additions (`addFacets`) are restricted to the diamond deployment phase to maintain proxy integrity and prevent runtime manipulation. +- Utilize `diamondFallback` for routing external calls to the correct facet implementation; ensure all necessary function selectors are registered. +- Access diamond storage via `getStorage` for read-only operations to understand the proxy's current state. + + +## Integration Notes + + +DiamondMod stores facet addresses and their associated function selectors. The `addFacets` function is intended for use only during the initial deployment of the diamond, as adding facets dynamically after deployment is not supported by this module. The `diamondFallback` function acts as the central dispatcher, inspecting the called function selector and forwarding the call to the corresponding facet's implementation. `getStorage` provides a snapshot of the diamond's internal storage layout, which can be crucial for understanding facet interactions and state. + + +
+ +
+ + diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json new file mode 100644 index 00000000..8b248747 --- /dev/null +++ b/website/docs/library/diamond/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Diamond Core", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Core diamond proxy functionality for ERC-2535 diamonds." + } +} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx new file mode 100644 index 00000000..5c3236f1 --- /dev/null +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -0,0 +1,150 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Example Diamond initialization and routing" +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Example Diamond initialization and routing + + + +- Manages initial facet registration and diamond ownership. +- Supports adding facets and their associated function selectors. +- Enables delegatecall routing by mapping function selectors to facet addresses. + + +## Overview + +The ExampleDiamond facet serves as a foundational component for Compose diamonds, demonstrating core initialization and routing mechanisms. It handles the initial setup of facets and establishes the diamond's owner, enabling delegatecall routing for all registered functions. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-protocol/diamond-sdk/src/interfaces/IDiamondCut.sol"; +import {IDiamondLoupe} from "@compose-protocol/diamond-sdk/src/interfaces/IDiamondLoupe.sol"; +import {ExampleDiamond} from "@compose-protocol/diamond-sdk/src/facets/ExampleDiamond.sol"; + +contract DeployExampleDiamond is IDiamondCut { + address public diamondProxy; + + function deploy() public { + // Example facet addresses and selectors (replace with actual deployment) + address facet1Address = address(0x123); + bytes4[] memory facet1Selectors = new bytes4[](1); + facet1Selectors[0] = ExampleDiamond.someFunction.selector; + + address facet2Address = address(0x456); + bytes4[] memory facet2Selectors = new bytes4[](1); + facet2Selectors[0] = ExampleDiamond.anotherFunction.selector; + + // Construct FacetCut data + FacetCut[] memory cuts = new FacetCut[](2); + cuts[0] = FacetCut({ + facetAddress: facet1Address, + action: FacetCutAction.Add, + functionSelectors: facet1Selectors + }); + cuts[1] = FacetCut({ + facetAddress: facet2Address, + action: FacetCutAction.Add, + functionSelectors: facet2Selectors + }); + + // Deploy Diamond Proxy and initialize with facets and owner + // Assume ExampleDiamond is the contract implementing the constructor logic for initialization + // In a real scenario, you would deploy a DiamondProxy contract and call its diamondCut function + // This example simplifies by directly calling a conceptual constructor-like initializer + // diamondProxy = address(new DiamondProxy(cuts, msg.sender)); // Conceptual deployment + + // For demonstration, assume ExampleDiamond itself is being deployed with initialization logic + // In practice, this would be a separate DiamondProxy contract + // diamondProxy = address(new ExampleDiamond(cuts, msg.sender)); // Conceptual deployment + } +} +`} + + +## Best Practices + + +- Initialize the diamond with all necessary facets and their function selectors during deployment. +- Ensure the owner is set correctly to manage diamond upgrades and facet additions. +- The `fallback` and `receive` functions should be handled by the diamond proxy itself, not typically within this facet. + + +## Security Considerations + + +Access control for ownership and facet management should be robust, typically managed by an `Ownable` pattern or a similar access control mechanism. Ensure that facet addresses provided during initialization are trusted and verified to prevent malicious code injection. Input validation on `FacetCut` data is crucial to prevent errors during initialization. + + +
+ +
+ + diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json new file mode 100644 index 00000000..d94c8663 --- /dev/null +++ b/website/docs/library/diamond/example/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "example", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "example components for Compose diamonds." + } +} diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx new file mode 100644 index 00000000..e729d865 --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -0,0 +1,151 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "ERC-165 interface detection and registration." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-165 interface detection and registration. + + + +- Implements the ERC-165 standard for interface detection. +- Manages a persistent registry of supported interfaces within diamond storage. +- Provides internal helper functions for facets to register and query interfaces. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod provides the standard ERC-165 interface detection mechanism for Compose diamonds. It manages the storage for supported interfaces and exposes functions to register new interfaces and retrieve the storage pointer, enabling facets to correctly implement the ERC-165 standard. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC165, ERC165Mod} from "@compose-protocol/diamond-contracts/modules/erc165/ERC165Mod.sol"; + +contract MyFacet { + function initialize() external { + // Register ERC721 interface during facet initialization + ERC165Mod.registerInterface(type(IERC721).interfaceId); + } + + function supportsInterface(bytes4 interfaceId) external view returns (bool) { + return ERC165Mod.supportsInterface(interfaceId); + } +}`} + + +## Best Practices + + +- Register all supported interfaces during facet initialization to ensure correct ERC-165 compliance. +- Call `ERC165Mod.supportsInterface(interfaceId)` within your facet's `supportsInterface` implementation for standard ERC-165 behavior. +- Ensure the `ERC165Mod` is correctly initialized and its storage is accessible. + + +## Integration Notes + + +The ERC165Mod utilizes a dedicated storage slot for its `ERC165Storage` struct. Facets interact with this storage via the `getStorage` internal function, which uses inline assembly to bind to the correct storage position. All facets should call `ERC165Mod.registerInterface` during their initialization to declare supported interfaces. The `supportsInterface` function on the diamond proxy will delegate to the ERC165Mod implementation. + + +
+ +
+ + diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json new file mode 100644 index 00000000..2f19715b --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-165", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-165 components for Compose diamonds." + } +} diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json new file mode 100644 index 00000000..62741de4 --- /dev/null +++ b/website/docs/library/interfaceDetection/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Interface Detection", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-165 interface detection support." + } +} diff --git a/website/docs/library/libraries/NonReentrancyMod.mdx b/website/docs/library/libraries/NonReentrancyMod.mdx new file mode 100644 index 00000000..98c6e151 --- /dev/null +++ b/website/docs/library/libraries/NonReentrancyMod.mdx @@ -0,0 +1,141 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "Prevent reentrant calls within diamond functions." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Prevent reentrant calls within diamond functions. + + + +- Prevents reentrant function calls by tracking execution state. +- Uses `enter()` to acquire a lock and `exit()` to release it. +- Emits a specific `Reentrancy` error upon detecting a reentrant attempt. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides essential mechanisms to prevent reentrant function calls. By implementing the `enter` and `exit` functions, facets can enforce that a function's execution is not interrupted by another call to the same function (or another function protected by the same mechanism) before it completes. This is critical for maintaining state integrity and security in complex smart contract interactions. + +--- + +## Storage + +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {NonReentrancyMod} from "@compose/modules/NonReentrancyMod.sol"; + +contract MyFacet { + NonReentrancyMod internal nonReentrancyMod; + + constructor(address _diamondProxy) { + nonReentrancyMod = NonReentrancyMod(_diamondProxy); + } + + function sensitiveOperation() external { + // Check if reentrancy is already active + if (!nonReentrancyMod.enter()) { + // ReentrancyGuard is already active, revert with Reentrancy error + revert NonReentrancyMod.Reentrancy(); + } + + // ... perform sensitive operations ... + + // Exit the ReentrancyGuard + nonReentrancyMod.exit(); + } +}`} + + +## Best Practices + + +- Always pair `enter()` calls with corresponding `exit()` calls to prevent deadlocks. +- Use the `NonReentrancyMod.Reentrancy()` custom error for clear revert reasons. +- Ensure `enter()` is called at the beginning and `exit()` at the end of sensitive operations. + + +## Integration Notes + + +NonReentrancyMod relies on a single storage slot within the diamond's storage layout to maintain its reentrancy lock status. Facets integrating this module must ensure that the `NonReentrancyMod` library is correctly initialized and that its `enter` and `exit` functions are called in the correct sequence. The state managed by this module is internal to the library and does not directly expose storage variables to other facets. + + +
+ +
+ + diff --git a/website/docs/library/libraries/_category_.json b/website/docs/library/libraries/_category_.json new file mode 100644 index 00000000..f20ad00a --- /dev/null +++ b/website/docs/library/libraries/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Utilities", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Utility libraries and helpers for diamond development." + } +} diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx new file mode 100644 index 00000000..447fae21 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -0,0 +1,666 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "Manages ERC-1155 multi-token standard functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 multi-token standard functionality. + + + +- Supports both single and batch token transfers (`safeTransferFrom`, `safeBatchTransferFrom`). +- Provides efficient balance checking via `balanceOf` and `balanceOfBatch`. +- Manages operator approvals for token management through `setApprovalForAll` and `isApprovedForAll`. + + +## Overview + +The ERC1155Facet provides a comprehensive implementation of the ERC-1155 standard for multi-token management within a Compose diamond. It enables tracking token balances, managing approvals, and performing token transfers efficiently through batched operations. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC1155/IERC1155Facet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; + +contract ERC1155Deployer { + address diamondAddress; + + function deploy() public { + // Assume diamondAddress is already set or initialized + IERC1155Facet erc1155Facet = IERC1155Facet(diamondAddress); + + // Example: Get balance of token ID 1 for address(this) + uint256 balance = erc1155Facet.balanceOf(address(this), 1); + + // Example: Set approval for operator + erc1155Facet.setApprovalForAll(address(0xOperator), true); + + // Example: Get URI for token ID 1 + string memory tokenUri = erc1155Facet.uri(1); + } +}`} + + +## Best Practices + + +- Initialize the `baseURI` and `tokenURIs` using the `setURI` function if token-specific URIs are required. +- Ensure necessary approvals are set via `setApprovalForAll` before attempting transfers on behalf of other accounts. +- Leverage `balanceOfBatch` and `safeBatchTransferFrom` for gas efficiency when dealing with multiple token types or accounts. + + +## Security Considerations + + +Access control for minting and burning is managed by separate facets. Ensure that the `safeTransferFrom` and `safeBatchTransferFrom` functions are called with valid sender and receiver addresses. The ERC1155InvalidSender, ERC1155InvalidReceiver, and ERC1155MissingApprovalForAll errors help prevent common transfer issues. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx new file mode 100644 index 00000000..9b483079 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -0,0 +1,607 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "Manages ERC-1155 token minting, burning, and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 token minting, burning, and transfers. + + + +- Supports both single and batch minting and burning operations. +- Implements safe transfer logic, including ERC1155Receiver validation for contract recipients. +- Provides functionality to set base and token-specific URIs for metadata. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module implements the core ERC-1155 token logic, enabling minting, burning, and safe transfers of multiple token types. It adheres to EIP-1155 standards, ensuring secure handling of token balances and receiver interactions. Integrating this module allows diamonds to function as robust ERC-1155 token issuers and managers. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Mod} from "@compose/modules/ERC1155Mod.sol"; + +contract MyDiamondFacet { + IERC1155Mod internal constant ERC1155 = IERC1155Mod(address(this)); + + function mintMyTokens(address to, uint256 id, uint256 amount) external { + ERC1155.mint(to, id, amount); + } + + function burnMyTokens(address from, uint256 id, uint256 amount) external { + ERC1155.burn(from, id, amount); + } + + function safeTransferMyTokens( + address from, + address to, + uint256 id, + uint256 amount, + bytes calldata data + ) external { + ERC1155.safeTransferFrom(from, to, id, amount, data); + } +}`} + + +## Best Practices + + +- Always validate `to` and `from` addresses before performing transfers or burns to prevent unintended state changes. +- Ensure the caller has sufficient approval or ownership before executing `safeTransferFrom` or `safeBatchTransferFrom`. +- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, and `ERC1155InvalidReceiver` errors gracefully in consuming facets. + + +## Integration Notes + + +The ERC1155Mod module interacts with a predefined storage slot within the diamond's storage layout to manage token balances, approvals, and URIs. Facets integrating this module will access this shared storage. The `getStorage` function provides direct access to this storage struct. Ensure that no other facets modify the relevant storage variables in ways that would violate ERC-1155 invariants. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json new file mode 100644 index 00000000..012a98bd --- /dev/null +++ b/website/docs/library/token/ERC1155/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-1155", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-1155 multi-token implementations." + } +} diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx new file mode 100644 index 00000000..a0b36f15 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -0,0 +1,247 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Burn ERC-20 tokens within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC-20 tokens within a diamond. + + + +- Supports burning tokens directly from the caller's balance. +- Enables burning tokens from other accounts via allowance. +- Adheres to ERC-20 standard by emitting `Transfer` events to the zero address. + + +## Overview + +The ERC20BurnFacet allows for the destruction of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account using their allowance, ensuring compliance with ERC-20 standards by emitting Transfer events to the zero address. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20BurnFacet} from "@compose/contracts/src/facets/ERC20/ERC20BurnFacet.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract ExampleDiamond { + address constant ERC20_BURN_FACET_ADDRESS = address(0x...); // Address of the deployed ERC20BurnFacet + + function burnMyTokens(address tokenAddress, uint256 amount) external { + ERC20BurnFacet(ERC20_BURN_FACET_ADDRESS).burn(tokenAddress, amount); + } + + function burnTokensFrom(address tokenAddress, address from, uint256 amount) external { + // Ensure allowance is set before calling burnFrom + // IERC20(tokenAddress).approve(address(this), allowance); // Example approval + ERC20BurnFacet(ERC20_BURN_FACET_ADDRESS).burnFrom(tokenAddress, from, amount); + } +}`} + + +## Best Practices + + +- Initialize the ERC20BurnFacet with the correct storage slot reference during diamond deployment. +- Ensure sufficient token balance and/or allowance before calling `burn` or `burnFrom` respectively. +- Verify that the token contract address passed to the functions is a valid ERC-20 compliant token. + + +## Security Considerations + + +The `burn` and `burnFrom` functions deduct tokens from balances or allowances. Ensure proper access control is implemented at the diamond level to restrict who can call these functions if required. Input validation for token amounts should be handled carefully to prevent unexpected behavior. Reentrancy is not a concern as these functions do not make external calls. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx new file mode 100644 index 00000000..b5405be4 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -0,0 +1,579 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Implements the ERC20 token standard for Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements the ERC20 token standard for Compose diamonds. + + + +- Implements all standard ERC20 functions. +- Leverages diamond storage pattern for state management. +- Designed for composability with other diamond facets. + + +## Overview + +The ERC20Facet provides a standard interface for fungible tokens within a Compose diamond. It manages token metadata, balances, allowances, and transfers, enabling composability with other diamond facets. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Facet} from "@compose/diamond/facets/ERC20Facet.sol"; +import {IDiamondCut} from "@compose/diamond/facets/DiamondCutFacet.sol"; + +contract DeployERC20Diamond { + function deploy() external { + // Assume diamondProxy is an instance of a deployed diamond contract + // Assume diamondCutFacetAddress is the address of the DiamondCutFacet + address diamondProxy; // Replace with actual diamond proxy address + address diamondCutFacetAddress; // Replace with actual DiamondCutFacet address + address erc20FacetAddress; // Replace with actual ERC20Facet contract address + + bytes32 erc20FacetCutSelector = keccak256("ERC20Facet"); // Example selector, actual is function signature hash + + IDiamondCut(diamondCutFacetAddress).diamondCut([ + (IDiamondCut.FacetCutOperation.ADD, erc20FacetAddress, erc20FacetCutSelector, "") // Assuming empty init data + ], address(0), ""); + + IERC20Facet erc20 = IERC20Facet(diamondProxy); + + // Example: Mint tokens to deployer + // The ERC20Facet itself might not have a mint function, but a separate facet could handle it. + // For demonstration, assume a hypothetical minting mechanism is in place or handled by another facet. + // erc20.mint(msg.sender, 1000 * 10**18); + + // Example: Check total supply + uint256 totalSupply = erc20.totalSupply(); + + // Example: Approve a spender + erc20.approve(address(1), 100 * 10**18); + + // Example: Check allowance + uint256 allowance = erc20.allowance(msg.sender, address(1)); + } +}`} + + +## Best Practices + + +- Initialize the ERC20Facet with the correct storage slot during diamond deployment. +- Access token data and perform transfers through the diamond proxy address. +- Ensure any token minting or burning logic is handled by a separate, appropriately permissioned facet. + + +## Security Considerations + + +Standard ERC20 security considerations apply, including checks for sufficient balance and allowance. Ensure that access control for token minting/burning is strictly enforced by a separate facet. Reentrancy is mitigated by the standard ERC20 transfer logic and diamond proxy pattern. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx new file mode 100644 index 00000000..701b0296 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -0,0 +1,418 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "ERC-20 token functionality for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token functionality for Compose diamonds + + + +- Implements core ERC-20 functions: `transfer`, `transferFrom`, `approve`. +- Supports `mint` and `burn` operations for supply management. +- Exposes internal storage via `getStorage` for direct access by authorized facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod module implements standard ERC-20 token operations. It provides essential functions for managing token balances, allowances, and total supply, enabling seamless integration of ERC-20 functionality into your Compose diamond. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "@compose/modules/ERC20/IERC20Mod.sol"; + +contract ERC20ConsumerFacet { + IERC20Mod public immutable erc20Mod; + + constructor(address _erc20ModAddress) { + erc20Mod = IERC20Mod(_erc20ModAddress); + } + + function consumeTransfer(address _from, address _to, uint256 _amount) external { + erc20Mod.transfer(_to, _amount); + } + + function consumeApprove(address _spender, uint256 _amount) external { + erc20Mod.approve(_spender, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20Mod facet is correctly initialized before use. +- Handle potential errors like `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` robustly. +- When upgrading, preserve the storage layout of the ERC20Mod to maintain state continuity. + + +## Integration Notes + + +The ERC20Mod utilizes a standard storage layout for ERC-20 state variables (balances, allowances, total supply). Facets interacting with this module should be aware of this layout for efficient storage access. The `getStorage` function provides a direct pointer to this struct, allowing for gas-efficient reads and writes when interacting with ERC-20 state. Ensure this facet is added to the diamond's facet registry. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json new file mode 100644 index 00000000..0b74f444 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 fungible token implementations." + } +} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..2fcbb8aa --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -0,0 +1,417 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "Facilitates cross-chain transfers for ERC20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Facilitates cross-chain transfers for ERC20 tokens. + + + +- Enables authorized cross-chain minting and burning of ERC20 tokens. +- Leverages diamond access control for `trusted-bridge` role validation. +- Provides internal checks for bridge authenticity. + + +## Overview + +The ERC20BridgeableFacet enables secure and authorized cross-chain token minting and burning operations. It integrates with the diamond's access control to ensure only trusted bridge addresses can perform these sensitive actions, maintaining integrity across different chains. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "../interfaces/IERC20BridgeableFacet.sol"; +import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol"; + +contract Deployer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function mintCrosschain(address _token, address _to, uint256 _amount) external { + bytes4 selector = IERC20BridgeableFacet.crosschainMint.selector; + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _token, _to, _amount)); + require(success, "Mint failed"); + } + + function burnCrosschain(address _token, address _from, uint256 _amount) external { + bytes4 selector = IERC20BridgeableFacet.crosschainBurn.selector; + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _token, _from, _amount)); + require(success, "Burn failed"); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is granted only to authorized bridge smart contracts or entities. +- Integrate with the diamond's access control mechanism to manage permissions for cross-chain operations. +- Use the `checkTokenBridge` internal function (or equivalent logic) within other facets to validate bridge caller authenticity before executing sensitive cross-chain logic. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are only callable by addresses holding the `trusted-bridge` role. The `checkTokenBridge` function enforces this role, preventing unauthorized cross-chain operations. Ensure the `trusted-bridge` role is managed carefully to prevent rug pulls or unauthorized token inflation/deflation. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..40440227 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -0,0 +1,419 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "Manage cross-chain ERC20 token transfers and burns." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage cross-chain ERC20 token transfers and burns. + + + +- Enables cross-chain minting and burning of ERC20 tokens. +- Enforces `trusted-bridge` role for sensitive cross-chain operations. +- Provides helper functions to access internal storage structs for ERC20 and AccessControl data. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides functionality for cross-chain ERC20 token operations, including minting and burning. It enforces access control to ensure only trusted bridge addresses can perform these sensitive actions, enhancing security for cross-chain interactions within a diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableMod} from "../interfaces/IERC20BridgeableMod.sol"; +import {DiamondStorage} from "../DiamondStorage.sol"; + +contract ERC20BridgeableFacet { + using DiamondStorage for IDiamondStorage; + + function _crosschainMint(address _token, address _to, uint256 _amount) external { + IDiamondStorage.accessControl().checkRole(IDiamondStorage.AccessControlStorage.ACCESS_CONTROL_STORAGE_SLOT, keccak256("trusted-bridge")); + IERC20BridgeableMod(address(this)).crosschainMint(_token, _to, _amount); + } + + function _crosschainBurn(address _token, address _from, uint256 _amount) external { + IDiamondStorage.accessControl().checkRole(IDiamondStorage.AccessControlStorage.ACCESS_CONTROL_STORAGE_SLOT, keccak256("trusted-bridge")); + IERC20BridgeableMod(address(this)).crosschainBurn(_token, _from, _amount); + } +}`} + + +## Best Practices + + +- Ensure that only addresses with the `trusted-bridge` role can call `crosschainMint` and `crosschainBurn` functions by implementing appropriate access control checks. +- Handle potential `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, and `ERC20InvalidReciever` errors to gracefully manage invalid bridge participants or addresses. +- Verify that the `_token` address passed to cross-chain functions is a valid ERC20 contract and that the sender has sufficient balance for `crosschainBurn` operations. + + +## Integration Notes + + +The `ERC20BridgeableMod` relies on the diamond storage pattern. It accesses `ERC20Storage` and `AccessControlStorage` from predefined storage slots within the diamond. The `checkTokenBridge` internal function verifies the caller's role against the `trusted-bridge` role stored in `AccessControlStorage`. Facets integrating with this module should ensure these storage slots are correctly initialized and managed. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json new file mode 100644 index 00000000..74afd31f --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Bridgeable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 Bridgeable extension for ERC-20 tokens." + } +} diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx new file mode 100644 index 00000000..ff89759e --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -0,0 +1,324 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "Manages ERC-20 token permits for EIP-2612 compliance." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-20 token permits for EIP-2612 compliance. + + + +- Implements EIP-2612 `permit` functionality for ERC-20 tokens. +- Provides `nonces` and `DOMAIN_SEPARATOR` for signature generation and validation. +- Enables gasless allowance approvals by users. + + +## Overview + +The ERC20PermitFacet enables EIP-2612 compliant token permits, allowing users to grant allowances to third parties via signed messages without on-chain transactions. It exposes functions to retrieve nonces, the domain separator, and to process permit signatures, enhancing composability for token interactions. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import {ERC20PermitFacet} from "../facets/ERC20PermitFacet.sol"; + +contract MyDiamond is IERC20Permit { + // ... diamond setup ... + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override { + address facetAddress = address(this); // or the actual facet address + bytes4 selector = bytes4(keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")); + + (bool success, ) = facetAddress.call(abi.encodeWithSelector(selector, owner, spender, value, deadline, v, r, s)); + require(success, "ERC20PermitFacet: permit call failed"); + } + + // ... other diamond functions ... +}`} + + +## Best Practices + + +- Store the DOMAIN_SEPARATOR and nonces in persistent storage within the diamond for predictable behavior and gas efficiency. +- Ensure the `deadline` parameter in `permit` is validated to prevent stale or overly long-lived permits. + + +## Security Considerations + + +The `permit` function is critical for managing allowances. Ensure that the signature verification logic correctly uses the `DOMAIN_SEPARATOR` and the owner's nonce to prevent replay attacks and unauthorized approvals. Access to `permit` should be controlled to prevent malicious actors from front-running or manipulating allowance grants. Input validation on `owner`, `spender`, `value`, and `deadline` is essential. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx new file mode 100644 index 00000000..20934d9c --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -0,0 +1,276 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "ERC-2612 Permit logic and domain separator" +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-2612 Permit logic and domain separator + + + +- Implements ERC-2612 permit logic for gasless token approvals. +- Generates a unique domain separator for signature replay protection. +- Provides functions to access internal permit storage for integration. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the necessary logic and storage for implementing ERC-2612 permit functionality, allowing gasless approvals for ERC-20 tokens. It defines the domain separator and handles the validation and application of permit signatures, enhancing composability and user experience by enabling off-chain authorization. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20PermitMod} from "@compose/modules/ERC20PermitMod.sol"; + +contract MyERC20Facet { + using ERC20PermitMod for ERC20PermitMod.PermitStorage; + + ERC20PermitMod.PermitStorage internal _permitStorage; + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Fetch domain separator from the module + bytes32 domainSeparator = ERC20PermitMod.DOMAIN_SEPARATOR(); + + // Validate and set allowance using the module's permit function + _permitStorage.permit(owner, spender, value, deadline, v, r, s, domainSeparator); + } + + // Other ERC20 functions... +}`} + + +## Best Practices + + +- Ensure the calling facet correctly emits the Approval event after a successful permit operation. +- Implement access control within the calling facet to restrict who can call the `permit` function if necessary. +- Verify that the `deadline` parameter in the permit signature is respected to prevent stale approvals. + + +## Integration Notes + + +The `ERC20PermitMod` module requires its `PermitStorage` struct to be integrated into the diamond's storage layout. Facets that utilize this module must instantiate and manage an instance of `ERC20PermitMod.PermitStorage`. The `permit` function within this module validates the signature and updates the allowance, but the `Approval` event must be emitted by the facet calling the module's function to comply with ERC-20 standards. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json new file mode 100644 index 00000000..54093a31 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Permit", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 Permit extension for ERC-20 tokens." + } +} diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json new file mode 100644 index 00000000..0b74f444 --- /dev/null +++ b/website/docs/library/token/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-20 fungible token implementations." + } +} diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx new file mode 100644 index 00000000..6660ea67 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -0,0 +1,529 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "Manages ERC-6909 token balances, allowances, and operator roles." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-6909 token balances, allowances, and operator roles. + + + +- Implements ERC-6909 standard for fungible and non-fungible tokens. +- Supports token transfers, allowance management, and operator roles. +- Provides direct access to underlying storage for efficient data retrieval. + + +## Overview + +The ERC6909Facet implements the ERC-6909 standard for fungible and non-fungible tokens within a Compose diamond. It provides core functionalities for managing token ownership, approvals, and operator relationships, enabling flexible token interactions and composability. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Facet} from "@compose/contracts/facets/ERC6909/IERC6909Facet.sol"; +import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; + +contract DeployERC6909 is IDiamondCut { + address constant ERC6909_FACET_ADDRESS = address(0x...); // Replace with actual deployed address + bytes4 constant TRANSFER_SELECTOR = IERC6909Facet.transfer.selector; + + function deployDiamond() external { + // ... diamond deployment logic ... + + // Add ERC6909Facet + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: ERC6909_FACET_ADDRESS, + action: IDiamondCut.Action.ADD, + selectors: bytes4[](byteset(TRANSFER_SELECTOR)) // Add all selectors for the facet + }); + diamondCut(cuts, address(0), ""); + } + + function interactWithERC6909(address _diamondAddress, address _to, uint256 _amount, bytes32 _id) external { + IERC6909Facet erc6909Facet = IERC6909Facet(_diamondAddress); + erc6909Facet.transfer(_to, _amount, _id); + } +}`} + + +## Best Practices + + +- Initialize the ERC6909Facet with appropriate initial token supply and ownership structures during diamond deployment. +- Access token-specific data (balance, allowance) via the diamond proxy address, leveraging the facet's selectors. +- Utilize `setOperator` judiciously to manage permissions for automated or delegated token transfers. + + +## Security Considerations + + +Ensure that access control for `transferFrom` and `approve` functions is correctly implemented and adheres to expected security patterns. Verify that `setOperator` does not grant unintended broad permissions. Input validation for `_id`, `_amount`, `_to`, and `_from` parameters should be robust to prevent unexpected state changes. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx new file mode 100644 index 00000000..ab2479fa --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -0,0 +1,522 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "Manages ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-6909 minimal multi-token logic. + + + +- Supports minting, burning, and transferring of multiple token IDs. +- Manages token approvals for spenders. +- Allows setting and removing operators for token holders. +- Adheres to ERC-6909 minimal multi-token standard. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic and storage for implementing the ERC-6909 standard. It enables functionalities like minting, burning, transferring, and managing operator approvals for multiple token types within a diamond. By abstracting this logic into a module, diamonds can easily integrate ERC-6909 compliance while adhering to Compose's principles of composability and storage reuse. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod} from "@compose/modules/ERC6909Mod.sol"; + +contract MyERC6909Facet { + IERC6909Mod internal immutable _erc6909Mod; + + constructor(address _erc6909ModAddress) { + _erc6909Mod = IERC6909Mod(_erc6909ModAddress); + } + + function mintToken(uint256 _id, uint256 _amount, address _to) external { + _erc6909Mod.mint(_id, _amount, _to); + } + + function approveToken(uint256 _id, address _spender, uint256 _amount) external { + _erc6909Mod.approve(_id, _spender, _amount); + } + + function transferToken(uint256 _id, address _from, address _to, uint256 _amount) external { + _erc6909Mod.transfer(_id, _from, _to, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `ERC6909Mod` contract is correctly initialized with its storage slot. +- Handle all custom errors emitted by the module functions to provide clear feedback to users. +- When implementing the `transfer` function, carefully check for operator status to allow seamless transfers by approved agents. + + +## Integration Notes + + +The `ERC6909Mod` utilizes a dedicated storage slot for its state, defined by `STORAGE_POSITION`. Facets interacting with this module should retrieve a pointer to the `ERC6909Storage` struct using the `getStorage` function. This allows for direct manipulation of module state, ensuring that all facets see a consistent view of the ERC-6909 data. The order of fields within the `ERC6909Storage` struct is critical and must be preserved for compatibility with the diamond storage pattern. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json new file mode 100644 index 00000000..5653b1ef --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-6909 minimal multi-token implementations." + } +} diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json new file mode 100644 index 00000000..5653b1ef --- /dev/null +++ b/website/docs/library/token/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-6909 minimal multi-token implementations." + } +} diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx new file mode 100644 index 00000000..4a6a059a --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -0,0 +1,218 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "Burn ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens within a Compose diamond. + + + +- Implements `burn` function to destroy ERC721 tokens. +- Emits standard `Transfer` event with `from` and `to` set to the zero address upon burning. +- Requires `ERC721NonexistentToken` and `ERC721InsufficientApproval` error checks. + + +## Overview + +The ERC721BurnFacet provides the functionality to destroy ERC721 tokens. It integrates with the diamond proxy pattern by exposing a `burn` function that modifies the token's state and emits standard ERC721 events. This facet is essential for managing the lifecycle of NFTs. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "./interfaces/IERC721BurnFacet.sol"; +import {IDiamondCut} from "./interfaces/IDiamondCut.sol"; + +contract Deployer { + function deploy() public { + // Assume diamond and diamondCut are deployed and initialized + address diamond = address(0x123); + IDiamondCut diamondCut = IDiamondCut(diamond); + + // Facet deployment and cut (simplified) + // address erc721BurnFacetAddress = new ERC721BurnFacet(); + // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + // cut[0] = IDiamondCut.FacetCut( + // erc721BurnFacetAddress, + // IDiamondCut.FacetCutAction.ADD, + // IDiamondCut.getSelectors(ERC721BurnFacet) + // ); + // diamondCut.diamondCut(cut, address(0), ""); + + IERC721BurnFacet burnFacet = IERC721BurnFacet(diamond); + + // Example: Burn token ID 1 + // Requires appropriate approval or ownership + // burnFacet.burn(1); + } +}`} + + +## Best Practices + + +- Ensure the ERC721BurnFacet is correctly cut into the diamond and its functions are accessible via the diamond proxy. +- Verify ownership or sufficient approval before calling the `burn` function to prevent unauthorized token destruction. + + +## Security Considerations + + +The `burn` function must be protected by appropriate access control mechanisms (ownership or delegated approval). Ensure that the caller has the necessary permissions to burn the specified token. The facet relies on the underlying ERC721 storage layout for token existence and approval checks. Reentrancy is not a concern as the function does not make external calls. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx new file mode 100644 index 00000000..47ab5dbd --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -0,0 +1,666 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "Manages ERC721 token ownership, approvals, and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC721 token ownership, approvals, and transfers. + + + +- Full ERC721 compliance for Non-Fungible Tokens. +- Supports both standard and safe token transfers. +- Manages token ownership, individual token approvals, and operator approvals. +- Provides essential metadata functions like name, symbol, and tokenURI. + + +## Overview + +The ERC721Facet provides the core functionality for managing Non-Fungible Tokens (NFTs) within a Compose diamond. It handles token ownership, approvals for individual tokens and operator roles, and facilitates both standard and safe token transfers according to the ERC721 standard. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose-protocol/diamond/contracts/facets/ERC721/IERC721Facet.sol"; + +contract ERC721Consumer { + IERC721Facet public immutable erc721Facet; + + constructor(address _diamondAddress) { + erc721Facet = IERC721Facet(_diamondAddress); + } + + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); + } + + function getTokenSymbol() external view returns (string memory) { + return erc721Facet.symbol(); + } + + function getOwner(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); + } + + function transferToken(address to, uint256 tokenId) external { + // Ensure caller is approved or owner + erc721Facet.transferFrom(msg.sender, to, tokenId); + } +}`} + + +## Best Practices + + +- Ensure the diamond proxy is initialized with the ERC721Facet to enable NFT functionality. +- Implement robust access control for functions that modify approvals or transfer tokens, typically requiring the caller to be the owner or an approved address. +- When upgrading or replacing the ERC721Facet, ensure compatibility with existing token data and user expectations. + + +## Security Considerations + + +Access control is paramount. Ensure that only the token owner or an explicitly approved address can initiate transfers or set approvals for a specific token. The `safeTransferFrom` functions include checks for receiver contract compatibility to mitigate reentrancy risks. Input validation on token IDs and addresses is crucial to prevent unexpected behavior or denial of service. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx new file mode 100644 index 00000000..b33a349d --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -0,0 +1,356 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "Manage ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC721 tokens within a Compose diamond. + + + +- Standard ERC721 token operations: mint, burn, transfer. +- Manages ERC721 state within the diamond's storage using a predefined slot. +- Enforces ERC721 ownership and approval checks during transfers. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod provides the core logic for ERC721 token management. It enables facets to mint, burn, and transfer tokens, adhering to the ERC721 standard. This module abstracts away the complexities of diamond storage for ERC721 state, allowing for robust and composable token functionality. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; +import {IDiamondCut} from "@compose/core/IDiamondCut.sol"; + +contract MyERC721Facet { + // Assume ERC721Mod is deployed and its address is known + address constant ERC721_MOD_ADDRESS = address(0x123); // Replace with actual address + + function safeMint(address _to, uint256 _tokenId) external { + IERC721Mod(ERC721_MOD_ADDRESS).mint(_to, _tokenId); + } + + function safeTransfer(address _from, address _to, uint256 _tokenId) external { + IERC721Mod(ERC721_MOD_ADDRESS).transferFrom(_from, _to, _tokenId); + } + + function safeBurn(uint256 _tokenId) external { + IERC721Mod(ERC721_MOD_ADDRESS).burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Mod` contract is correctly initialized and accessible via its address. +- Always validate `_to` addresses in `mint` and `transferFrom` calls to prevent sending tokens to zero addresses. +- Handle potential errors like `ERC721IncorrectOwner`, `ERC721NonexistentToken`, and `ERC721InvalidReceiver` appropriately in calling facets. + + +## Integration Notes + + +The ERC721Mod interacts with diamond storage at a fixed slot to manage its internal `ERC721Storage` struct. Facets calling functions within this module will implicitly read from and write to this shared storage. Ensure that no other facets or modules attempt to use the same storage slot to avoid state conflicts. The `getStorage` function can be used by facets to inspect the current ERC721 state. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json new file mode 100644 index 00000000..5fdbd55a --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-721 non-fungible token implementations." + } +} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..76a63c86 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,226 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "Burn ERC721 tokens and manage enumeration state." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens and manage enumeration state. + + + +- Allows burning of ERC721 tokens. +- Integrates with enumeration logic to remove burned tokens from tracking. +- Emits a `Transfer` event with `from` and `to` addresses set to the zero address for burned tokens. + + +## Overview + +The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens within a Compose diamond. It ensures that burned tokens are correctly removed from enumeration tracking, maintaining the integrity of the token supply and ownership data. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurnFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; + +contract BurnExample { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnToken(uint256 _tokenId) external { + IERC721EnumerableBurnFacet(diamondAddress).burn(_tokenId); + } + + // Example of getting storage (for inspection or advanced logic) + function getFacetStorage() external view returns (bytes memory) { + return IERC721EnumerableBurnFacet(diamondAddress).getStorage(); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented at the diamond level before calling the `burn` function to prevent unauthorized token destruction. +- Verify that the token ID provided to `burn` actually exists to avoid `ERC721NonexistentToken` errors. +- Understand that burning a token is irreversible and removes it from all enumeration. + + +## Security Considerations + + +The `burn` function requires that the caller either be the owner of the token or have been granted sufficient approval. Failure to meet these conditions will result in an `ERC721InsufficientApproval` error. Additionally, calling `burn` on a non-existent token will revert with `ERC721NonexistentToken`. Ensure that the diamond's access control layer correctly restricts who can call these functions. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..fd78c62f --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -0,0 +1,748 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "Enumerable ERC-721 implementation for token tracking." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enumerable ERC-721 implementation for token tracking. + + + +- Full ERC-721 compliance with enumerable extensions. +- Efficient on-chain tracking of token supply and ownership. +- Provides indexed access to tokens owned by an address. + + +## Overview + +This facet provides a complete ERC-721 implementation with enumerable extensions, enabling efficient tracking of token supply, ownership counts, and individual token ownership by index. It ensures standard ERC-721 functionality while adding robust on-chain querying capabilities. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; + +contract MyDiamond { + // ... diamond setup + + function name() external view returns (string memory) { + return ERC721EnumerableFacet.name(); + } + + function symbol() external view returns (string memory) { + return ERC721EnumerableFacet.symbol(); + } + + function tokenURI(uint256 tokenId) external view returns (string memory) { + return ERC721EnumerableFacet.tokenURI(tokenId); + } + + function totalSupply() external view returns (uint256) { + return ERC721EnumerableFacet.totalSupply(); + } + + function balanceOf(address owner) external view returns (uint256) { + return ERC721EnumerableFacet.balanceOf(owner); + } + + function ownerOf(uint256 tokenId) external view returns (address) { + return ERC721EnumerableFacet.ownerOf(tokenId); + } + + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) { + return ERC721EnumerableFacet.tokenOfOwnerByIndex(owner, index); + } + + // ... other functions +}`} + + +## Best Practices + + +- Ensure proper initialization of the ERC721EnumerableFacet storage within the diamond's initialization logic. +- Access token-specific information using `ownerOf`, `tokenURI`, and `tokenOfOwnerByIndex` for clarity and gas efficiency. +- Leverage `balanceOf` and `totalSupply` for quick on-chain state checks. + + +## Security Considerations + + +Input validation is crucial for all token ID and address parameters to prevent errors and unexpected behavior. Ensure that approvals are managed correctly to maintain ownership integrity. Reentrancy is not a direct concern for read-only enumerable functions, but state-changing functions like `transferFrom` and `safeTransferFrom` must follow standard ERC-721 reentrancy guards. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..1a815a84 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -0,0 +1,348 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "Manages ERC-721 tokens with enumeration support." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-721 tokens with enumeration support. + + + +- Supports minting new ERC-721 tokens and adding them to enumeration lists. +- Enables burning of existing ERC-721 tokens, removing them from enumeration. +- Manages token transfers while updating internal ownership and enumeration data. +- Provides a `getStorage` function to access the internal enumerable storage struct. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic for implementing enumerable ERC-721 functionality within a Compose diamond. It handles token minting, burning, and transfers while maintaining accurate lists of all owned tokens for each address and the total supply. This ensures compliance with ERC-721 standards and allows for efficient querying of token ownership. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "@compose/modules/erc721/ERC721EnumerableMod.sol"; + +contract MyERC721Facet { + IERC721EnumerableMod internal immutable erc721Mod; + + constructor(address _diamondProxy) { + erc721Mod = IERC721EnumerableMod(_diamondProxy); + } + + function safeMint(address to, uint256 tokenId) external { + // Add any custom checks here before minting + erc721Mod.mint(to, tokenId); + // Add any custom logic after minting here + } + + function safeTransferFrom(address from, address to, uint256 tokenId) external { + // Add any custom checks here before transferring + erc721Mod.transferFrom(from, to, tokenId); + // Add any custom logic after transferring here + } + + function destroyToken(uint256 tokenId) external { + // Add any custom checks here before burning + erc721Mod.burn(tokenId); + // Add any custom logic after burning here + } +}`} + + +## Best Practices + + +- Ensure the `ERC721EnumerableMod` facet is correctly initialized and accessible via the diamond proxy. +- Implement necessary access control and validation logic within your custom facets before calling module functions like `mint`, `burn`, or `transferFrom`. +- Handle potential reverts from module functions, such as `ERC721IncorrectOwner` or `ERC721NonexistentToken`, appropriately in your facet logic. + + +## Integration Notes + + +The `ERC721EnumerableMod` module interacts with a predefined storage slot within the diamond's storage layout to manage the state of enumerable ERC-721 tokens. This includes mapping token IDs to owners, tracking token ownership for enumeration, and maintaining the total supply. Facets integrating with this module should be aware that state changes made through the module's functions (mint, burn, transferFrom) directly affect the diamond's storage and are immediately visible to all other facets. The order of storage variables within the `ERC721EnumerableStorage` struct is critical and must not be altered to maintain compatibility. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json new file mode 100644 index 00000000..6ab22b34 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721 Enumerable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-721 Enumerable extension for ERC-721 tokens." + } +} diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json new file mode 100644 index 00000000..5fdbd55a --- /dev/null +++ b/website/docs/library/token/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-721 non-fungible token implementations." + } +} diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx new file mode 100644 index 00000000..9a2a8bfc --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -0,0 +1,189 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "Handles royalty information for tokens and sales." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Handles royalty information for tokens and sales. + + + +- Implements ERC-2981 `royaltyInfo` standard. +- Supports token-specific royalty configurations. +- Falls back to a default royalty percentage. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, providing a standardized way to query royalty information for NFTs. It allows for setting token-specific royalties and falls back to a default royalty, ensuring creators are compensated on secondary sales. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyFacet} from "@compose-protocol/diamond/contracts/facets/Royalty/IRoyaltyFacet.sol"; + +contract RoyaltyConsumer { + address public diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { + bytes4 selector = IRoyaltyFacet.royaltyInfo.selector; + (receiver, royaltyAmount) = IRoyaltyFacet(diamondAddress).royaltyInfo(selector, _tokenId, _salePrice); + return (receiver, royaltyAmount); + } +}`} + + +## Best Practices + + +- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. +- Ensure the `royaltyInfo` function is callable by external marketplaces or consumers. +- Store royalty configurations off-chain or in a separate, permissioned facet if dynamic updates are required. + + +## Security Considerations + + +Access to modify royalty settings (if implemented in a separate facet) must be strictly controlled. Ensure the calculation of royalty amounts prevents integer overflow or underflow. The `royaltyInfo` function itself is read-only and poses no direct reentrancy risk. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx new file mode 100644 index 00000000..4286283e --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -0,0 +1,365 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "Manage ERC-2981 royalties for tokens and defaults." +gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-2981 royalties for tokens and defaults. + + + +- Implements ERC-2981 standard for on-chain royalty attribution. +- Supports both token-specific and default royalty configurations. +- Provides functions to query royalty information based on token ID and sale price. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides a standardized implementation for ERC-2981 royalty payments. It allows setting both default royalties applicable to all tokens and specific royalties for individual tokens, ensuring compliance with the royalty standard. This enhances composability by providing a predictable mechanism for creators to receive royalties on secondary sales. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "../interfaces/IRoyaltyMod.sol"; +import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol"; + +contract MyFacet is IERC2981 { + address immutable DIAMOND_ADDRESS; + IRoyaltyMod private royaltyMod; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + royaltyMod = IRoyaltyMod(diamondAddress); + } + + /** + * @notice Gets royalty information for a token and sale price. + * @param tokenId The ID of the token. + * @param salePrice The sale price of the token. + * @return receiver The address receiving the royalty. + * @return royaltyAmount The amount of royalty. + */ + function royaltyInfo(uint256 tokenId, uint256 salePrice) external view override returns (address receiver, uint256 royaltyAmount) { + (receiver, royaltyAmount) = royaltyMod.royaltyInfo(tokenId, salePrice); + } + + /** + * @notice Sets royalty information for a specific token. + * @param tokenId The ID of the token. + * @param receiver The address receiving the royalty. + * @param feeBasisPoints The royalty fee in basis points (e.g., 100 for 1%). + */ + function setTokenRoyalty(uint256 tokenId, address receiver, uint16 feeBasisPoints) external { + royaltyMod.setTokenRoyalty(tokenId, receiver, feeBasisPoints); + } + + /** + * @notice Sets default royalty information for all tokens. + * @param receiver The address receiving the default royalty. + * @param feeBasisPoints The default royalty fee in basis points. + */ + function setDefaultRoyalty(address receiver, uint16 feeBasisPoints) external { + royaltyMod.setDefaultRoyalty(receiver, feeBasisPoints); + } +} +`} + + +## Best Practices + + +- Use `setTokenRoyalty` to configure specific token royalties and `setDefaultRoyalty` for general fallback, ensuring a clear hierarchy. +- Validate receiver addresses and fee basis points rigorously before setting royalties to prevent errors and unexpected payouts. +- Handle `ERC2981InvalidDefaultRoyalty`, `ERC2981InvalidDefaultRoyaltyReceiver`, `ERC2981InvalidTokenRoyalty`, and `ERC2981InvalidTokenRoyaltyReceiver` errors to gracefully manage invalid royalty configurations. + + +## Integration Notes + + +The RoyaltyMod utilizes a dedicated storage slot for its state, containing `DefaultRoyalty` and `TokenRoyalty` mappings. The `getStorage` function provides direct access to this struct. Facets interacting with royalty logic should call the module's functions to ensure consistent and correct handling of royalty data. Changes to default or token-specific royalties are immediately reflected in subsequent `royaltyInfo` calls. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json new file mode 100644 index 00000000..95a86a02 --- /dev/null +++ b/website/docs/library/token/Royalty/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Royalty", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "ERC-2981 royalty standard implementations." + } +} diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json new file mode 100644 index 00000000..a4461cfd --- /dev/null +++ b/website/docs/library/token/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Token Standards", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Token standard implementations for Compose diamonds." + } +} From 32bcbbc9d7fc1a9b892fef4f7f77342832806226 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 15:49:41 -0500 Subject: [PATCH 43/68] imoprove category-generator --- .../generate-docs-utils/category-generator.js | 80 ++++++++++++++++--- website/sidebars.js | 4 +- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/.github/scripts/generate-docs-utils/category-generator.js b/.github/scripts/generate-docs-utils/category-generator.js index 3a310078..ed4f769e 100644 --- a/.github/scripts/generate-docs-utils/category-generator.js +++ b/.github/scripts/generate-docs-utils/category-generator.js @@ -29,6 +29,7 @@ const CATEGORY_LABELS = { token: 'Token Standards', diamond: 'Diamond Core', libraries: 'Utilities', + utils: 'Utilities', interfaceDetection: 'Interface Detection', // Token subcategories @@ -56,6 +57,7 @@ const CATEGORY_DESCRIPTIONS = { token: 'Token standard implementations for Compose diamonds.', diamond: 'Core diamond proxy functionality for ERC-2535 diamonds.', libraries: 'Utility libraries and helpers for diamond development.', + utils: 'Utility libraries and helpers for diamond development.', interfaceDetection: 'ERC-165 interface detection support.', // Token subcategories @@ -83,6 +85,7 @@ const CATEGORY_POSITIONS = { access: 2, token: 3, libraries: 4, + utils: 4, interfaceDetection: 5, // Token subcategories @@ -266,6 +269,38 @@ function scanSourceStructure() { // Category File Generation // ============================================================================ +/** + * Map source directory name to docs directory name + * @param {string} srcName - Source directory name + * @returns {string} Documentation directory name + */ +function mapDirectoryName(srcName) { + // Map libraries -> utils for URL consistency + if (srcName === 'libraries') { + return 'utils'; + } + return srcName; +} + +/** + * Compute slug from output directory path + * @param {string} outputDir - Full output directory path + * @param {string} libraryDir - Base library directory + * @returns {string} Slug path (e.g., '/docs/library/access') + */ +function computeSlug(outputDir, libraryDir) { + const relativePath = path.relative(libraryDir, outputDir); + + if (!relativePath || relativePath.startsWith('..')) { + // Root library directory + return '/docs/library'; + } + + // Convert path separators and create slug + const normalizedPath = relativePath.replace(/\\/g, '/'); + return `/docs/library/${normalizedPath}`; +} + /** * Create a _category_.json file for a directory * @param {string} outputDir - Directory to create category file in @@ -276,16 +311,21 @@ function scanSourceStructure() { */ function createCategoryFile(outputDir, name, relativePath, depth) { const categoryFile = path.join(outputDir, '_category_.json'); + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; // Don't overwrite existing category files (allows manual customization) if (fs.existsSync(categoryFile)) { return false; } + // Get the actual directory name from the output path (may be mapped, e.g., utils instead of libraries) + const actualDirName = path.basename(outputDir); const parentParts = relativePath.split('/').slice(0, -1); - const label = generateLabel(name); - const position = getCategoryPosition(name, depth); - const description = generateDescription(name, parentParts); + // Use actual directory name for label generation (supports both original and mapped names) + const label = generateLabel(actualDirName); + const position = getCategoryPosition(actualDirName, depth); + const description = generateDescription(actualDirName, parentParts); + const slug = computeSlug(outputDir, libraryDir); const category = { label, @@ -294,6 +334,7 @@ function createCategoryFile(outputDir, name, relativePath, depth) { collapsed: true, // Collapse all categories by default link: { type: 'generated-index', + slug, description, }, }; @@ -324,6 +365,7 @@ function ensureBaseCategory(libraryDir) { collapsed: true, // Collapse base Library category by default link: { type: 'generated-index', + slug: '/docs/library', title: 'Library Reference', description: 'API reference for all Compose modules and facets.', }, @@ -342,6 +384,7 @@ function ensureBaseCategory(libraryDir) { /** * Compute output path for a source file * Mirrors the src/ structure in website/docs/library/ + * Applies directory name mapping (e.g., libraries -> utils) * * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/AccessControlMod.sol') * @returns {object} Output path information @@ -358,18 +401,21 @@ function computeOutputPath(solFilePath) { const parts = relativePath.split('/'); const fileName = parts.pop(); - const outputDir = path.join(libraryDir, ...parts); + // Map directory names (e.g., libraries -> utils) + const mappedParts = parts.map(part => mapDirectoryName(part)); + + const outputDir = path.join(libraryDir, ...mappedParts); const outputFile = path.join(outputDir, `${fileName}.mdx`); return { outputDir, outputFile, - relativePath: parts.join('/'), + relativePath: mappedParts.join('/'), fileName, - category: parts[0] || '', - subcategory: parts[1] || '', - fullRelativePath: relativePath, - depth: parts.length, + category: mappedParts[0] || '', + subcategory: mappedParts[1] || '', + fullRelativePath: mappedParts.join('/'), + depth: mappedParts.length, }; } @@ -399,6 +445,7 @@ function ensureCategoryFiles(outputDir) { for (let i = 0; i < parts.length; i++) { currentPath = path.join(currentPath, parts[i]); const segment = parts[i]; + // Use the mapped path for the relative path (already mapped in computeOutputPath) const relPath = parts.slice(0, i + 1).join('/'); createCategoryFile(currentPath, segment, relPath, i + 1); @@ -436,18 +483,23 @@ function syncDocsStructure() { ); for (const [relativePath, info] of sortedPaths) { - const outputDir = path.join(libraryDir, relativePath); + // Map directory names in the path (e.g., libraries -> utils) + const pathParts = relativePath.split('/'); + const mappedPathParts = pathParts.map(part => mapDirectoryName(part)); + const mappedRelativePath = mappedPathParts.join('/'); + const outputDir = path.join(libraryDir, mappedPathParts); + const wasCreated = createCategoryFile( outputDir, info.name, - relativePath, + mappedRelativePath, info.depth ); if (wasCreated) { - created.push(relativePath); + created.push(mappedRelativePath); } else { - existing.push(relativePath); + existing.push(mappedRelativePath); } } @@ -475,6 +527,8 @@ module.exports = { generateDescription, getCategoryPosition, containsSolFiles, + mapDirectoryName, + computeSlug, // For extending/customizing CATEGORY_LABELS, diff --git a/website/sidebars.js b/website/sidebars.js index eb0796e8..a9b1d7df 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -60,12 +60,12 @@ const sidebars = { }, { type: 'category', - label: 'Contracts', + label: 'Library', collapsed: true, items: [ { type: 'autogenerated', - dirName: 'contracts', + dirName: 'library', }, ], }, From 859edf4d85c0ecba592172bfb5c88fd1c289c326 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 15:50:13 -0500 Subject: [PATCH 44/68] remove lib pages --- website/docs/_category_.json | 5 +- .../access/AccessControl/_category_.json | 3 +- .../AccessControlPausable/_category_.json | 3 +- .../AccessControlTemporal/_category_.json | 3 +- .../contracts/access/Owner/_category_.json | 3 +- .../access/OwnerTwoSteps/_category_.json | 3 +- website/docs/contracts/access/_category_.json | 3 +- .../docs/contracts/diamond/_category_.json | 3 +- .../contracts/diamond/example/_category_.json | 3 +- .../interfaceDetection/ERC165/_category_.json | 3 +- .../interfaceDetection/_category_.json | 3 +- .../docs/contracts/libraries/_category_.json | 3 +- .../contracts/token/ERC1155/_category_.json | 3 +- .../token/ERC20/ERC20/_category_.json | 3 +- .../ERC20/ERC20Bridgeable/_category_.json | 3 +- .../token/ERC20/ERC20Permit/_category_.json | 3 +- .../contracts/token/ERC20/_category_.json | 3 +- .../token/ERC6909/ERC6909/_category_.json | 3 +- .../contracts/token/ERC6909/_category_.json | 3 +- .../token/ERC721/ERC721/_category_.json | 3 +- .../ERC721/ERC721Enumerable/_category_.json | 3 +- .../contracts/token/ERC721/_category_.json | 3 +- .../contracts/token/Royalty/_category_.json | 3 +- website/docs/contracts/token/_category_.json | 3 +- website/docs/contribution/_category_.json | 5 +- website/docs/getting-started/_category_.json | 4 +- website/docs/library/_category_.json | 11 - .../AccessControl/AccessControlFacet.mdx | 566 ------------- .../access/AccessControl/AccessControlMod.mdx | 443 ----------- .../access/AccessControl/_category_.json | 10 - .../AccessControlPausableFacet.mdx | 373 --------- .../AccessControlPausableMod.mdx | 381 --------- .../AccessControlPausable/_category_.json | 10 - .../AccessControlTemporalFacet.mdx | 446 ----------- .../AccessControlTemporalMod.mdx | 479 ----------- .../AccessControlTemporal/_category_.json | 10 - .../docs/library/access/Owner/OwnerFacet.mdx | 209 ----- .../docs/library/access/Owner/OwnerMod.mdx | 253 ------ .../docs/library/access/Owner/_category_.json | 10 - .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 292 ------- .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 296 ------- .../access/OwnerTwoSteps/_category_.json | 10 - website/docs/library/access/_category_.json | 10 - .../docs/library/diamond/DiamondCutFacet.mdx | 419 ---------- .../docs/library/diamond/DiamondCutMod.mdx | 393 --------- .../library/diamond/DiamondLoupeFacet.mdx | 250 ------ website/docs/library/diamond/DiamondMod.mdx | 238 ------ website/docs/library/diamond/_category_.json | 10 - .../diamond/example/ExampleDiamond.mdx | 150 ---- .../library/diamond/example/_category_.json | 10 - .../interfaceDetection/ERC165/ERC165Mod.mdx | 151 ---- .../interfaceDetection/ERC165/_category_.json | 10 - .../interfaceDetection/_category_.json | 10 - .../library/libraries/NonReentrancyMod.mdx | 141 ---- .../docs/library/libraries/_category_.json | 10 - .../library/token/ERC1155/ERC1155Facet.mdx | 666 ---------------- .../docs/library/token/ERC1155/ERC1155Mod.mdx | 607 -------------- .../library/token/ERC1155/_category_.json | 10 - .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 247 ------ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 579 -------------- .../library/token/ERC20/ERC20/ERC20Mod.mdx | 418 ---------- .../library/token/ERC20/ERC20/_category_.json | 10 - .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 417 ---------- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 419 ---------- .../ERC20/ERC20Bridgeable/_category_.json | 10 - .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 324 -------- .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 276 ------- .../token/ERC20/ERC20Permit/_category_.json | 10 - .../docs/library/token/ERC20/_category_.json | 10 - .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 529 ------------- .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 522 ------------ .../token/ERC6909/ERC6909/_category_.json | 10 - .../library/token/ERC6909/_category_.json | 10 - .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 218 ----- .../token/ERC721/ERC721/ERC721Facet.mdx | 666 ---------------- .../library/token/ERC721/ERC721/ERC721Mod.mdx | 356 --------- .../token/ERC721/ERC721/_category_.json | 10 - .../ERC721EnumerableBurnFacet.mdx | 226 ------ .../ERC721EnumerableFacet.mdx | 748 ------------------ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 348 -------- .../ERC721/ERC721Enumerable/_category_.json | 10 - .../docs/library/token/ERC721/_category_.json | 10 - .../library/token/Royalty/RoyaltyFacet.mdx | 189 ----- .../docs/library/token/Royalty/RoyaltyMod.mdx | 365 --------- .../library/token/Royalty/_category_.json | 10 - website/docs/library/token/_category_.json | 10 - 86 files changed, 54 insertions(+), 13870 deletions(-) delete mode 100644 website/docs/library/_category_.json delete mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx delete mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx delete mode 100644 website/docs/library/access/AccessControl/_category_.json delete mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/_category_.json delete mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json delete mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx delete mode 100644 website/docs/library/access/Owner/OwnerMod.mdx delete mode 100644 website/docs/library/access/Owner/_category_.json delete mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json delete mode 100644 website/docs/library/access/_category_.json delete mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondCutMod.mdx delete mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondMod.mdx delete mode 100644 website/docs/library/diamond/_category_.json delete mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx delete mode 100644 website/docs/library/diamond/example/_category_.json delete mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx delete mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json delete mode 100644 website/docs/library/interfaceDetection/_category_.json delete mode 100644 website/docs/library/libraries/NonReentrancyMod.mdx delete mode 100644 website/docs/library/libraries/_category_.json delete mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx delete mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx delete mode 100644 website/docs/library/token/ERC1155/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json delete mode 100644 website/docs/library/token/ERC20/_category_.json delete mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json delete mode 100644 website/docs/library/token/ERC6909/_category_.json delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json delete mode 100644 website/docs/library/token/ERC721/_category_.json delete mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx delete mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx delete mode 100644 website/docs/library/token/Royalty/_category_.json delete mode 100644 website/docs/library/token/_category_.json diff --git a/website/docs/_category_.json b/website/docs/_category_.json index 8226f67a..e945adbe 100644 --- a/website/docs/_category_.json +++ b/website/docs/_category_.json @@ -3,8 +3,9 @@ "position": 1, "link": { "type": "generated-index", - "description": "Learn how to contribute to Compose" + "description": "Learn how to contribute to Compose", + "slug": "/docs" }, "collapsible": true, "collapsed": true -} \ No newline at end of file +} diff --git a/website/docs/contracts/access/AccessControl/_category_.json b/website/docs/contracts/access/AccessControl/_category_.json index 25db9246..f152df3d 100644 --- a/website/docs/contracts/access/AccessControl/_category_.json +++ b/website/docs/contracts/access/AccessControl/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "Role-based access control (RBAC) pattern." + "description": "Role-based access control (RBAC) pattern.", + "slug": "/docs/contracts/access/AccessControl" } } diff --git a/website/docs/contracts/access/AccessControlPausable/_category_.json b/website/docs/contracts/access/AccessControlPausable/_category_.json index ab207a3c..c011f583 100644 --- a/website/docs/contracts/access/AccessControlPausable/_category_.json +++ b/website/docs/contracts/access/AccessControlPausable/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "RBAC with pause functionality." + "description": "RBAC with pause functionality.", + "slug": "/docs/contracts/access/AccessControlPausable" } } diff --git a/website/docs/contracts/access/AccessControlTemporal/_category_.json b/website/docs/contracts/access/AccessControlTemporal/_category_.json index 72012bb3..72b34fb4 100644 --- a/website/docs/contracts/access/AccessControlTemporal/_category_.json +++ b/website/docs/contracts/access/AccessControlTemporal/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "Time-limited role-based access control." + "description": "Time-limited role-based access control.", + "slug": "/docs/contracts/access/AccessControlTemporal" } } diff --git a/website/docs/contracts/access/Owner/_category_.json b/website/docs/contracts/access/Owner/_category_.json index 274b507b..52f6a99d 100644 --- a/website/docs/contracts/access/Owner/_category_.json +++ b/website/docs/contracts/access/Owner/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "Single-owner access control pattern." + "description": "Single-owner access control pattern.", + "slug": "/docs/contracts/access/Owner" } } diff --git a/website/docs/contracts/access/OwnerTwoSteps/_category_.json b/website/docs/contracts/access/OwnerTwoSteps/_category_.json index 52fea0d0..44617d2d 100644 --- a/website/docs/contracts/access/OwnerTwoSteps/_category_.json +++ b/website/docs/contracts/access/OwnerTwoSteps/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "Two-step ownership transfer pattern." + "description": "Two-step ownership transfer pattern.", + "slug": "/docs/contracts/access/OwnerTwoSteps" } } diff --git a/website/docs/contracts/access/_category_.json b/website/docs/contracts/access/_category_.json index da73185e..a3b4345e 100644 --- a/website/docs/contracts/access/_category_.json +++ b/website/docs/contracts/access/_category_.json @@ -5,6 +5,7 @@ "collapsed": false, "link": { "type": "generated-index", - "description": "Access control patterns for permission management in Compose diamonds." + "description": "Access control patterns for permission management in Compose diamonds.", + "slug": "/docs/contracts/access" } } diff --git a/website/docs/contracts/diamond/_category_.json b/website/docs/contracts/diamond/_category_.json index 69336647..dce4984f 100644 --- a/website/docs/contracts/diamond/_category_.json +++ b/website/docs/contracts/diamond/_category_.json @@ -5,6 +5,7 @@ "collapsed": false, "link": { "type": "generated-index", - "description": "Core diamond proxy functionality for ERC-2535 diamonds." + "description": "Core diamond proxy functionality for ERC-2535 diamonds.", + "slug": "/docs/contracts/diamond" } } diff --git a/website/docs/contracts/diamond/example/_category_.json b/website/docs/contracts/diamond/example/_category_.json index d94c8663..b3817d91 100644 --- a/website/docs/contracts/diamond/example/_category_.json +++ b/website/docs/contracts/diamond/example/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "example components for Compose diamonds." + "description": "example components for Compose diamonds.", + "slug": "/docs/contracts/diamond/example" } } diff --git a/website/docs/contracts/interfaceDetection/ERC165/_category_.json b/website/docs/contracts/interfaceDetection/ERC165/_category_.json index 2f19715b..2fbfe2da 100644 --- a/website/docs/contracts/interfaceDetection/ERC165/_category_.json +++ b/website/docs/contracts/interfaceDetection/ERC165/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-165 components for Compose diamonds." + "description": "ERC-165 components for Compose diamonds.", + "slug": "/docs/contracts/interfaceDetection/ERC165" } } diff --git a/website/docs/contracts/interfaceDetection/_category_.json b/website/docs/contracts/interfaceDetection/_category_.json index 08fc60ce..7fe0636c 100644 --- a/website/docs/contracts/interfaceDetection/_category_.json +++ b/website/docs/contracts/interfaceDetection/_category_.json @@ -5,6 +5,7 @@ "collapsed": false, "link": { "type": "generated-index", - "description": "ERC-165 interface detection support." + "description": "ERC-165 interface detection support.", + "slug": "/docs/contracts/interfaceDetection" } } diff --git a/website/docs/contracts/libraries/_category_.json b/website/docs/contracts/libraries/_category_.json index 0b5ad8cc..73e90bb3 100644 --- a/website/docs/contracts/libraries/_category_.json +++ b/website/docs/contracts/libraries/_category_.json @@ -5,6 +5,7 @@ "collapsed": false, "link": { "type": "generated-index", - "description": "Utility libraries and helpers for diamond development." + "description": "Utility libraries and helpers for diamond development.", + "slug": "/docs/contracts/libraries" } } diff --git a/website/docs/contracts/token/ERC1155/_category_.json b/website/docs/contracts/token/ERC1155/_category_.json index 012a98bd..71fe6a13 100644 --- a/website/docs/contracts/token/ERC1155/_category_.json +++ b/website/docs/contracts/token/ERC1155/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-1155 multi-token implementations." + "description": "ERC-1155 multi-token implementations.", + "slug": "/docs/contracts/token/ERC1155" } } diff --git a/website/docs/contracts/token/ERC20/ERC20/_category_.json b/website/docs/contracts/token/ERC20/ERC20/_category_.json index 0b74f444..9e5771df 100644 --- a/website/docs/contracts/token/ERC20/ERC20/_category_.json +++ b/website/docs/contracts/token/ERC20/ERC20/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-20 fungible token implementations." + "description": "ERC-20 fungible token implementations.", + "slug": "/docs/contracts/token/ERC20/ERC20" } } diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json index 74afd31f..c894aef5 100644 --- a/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json +++ b/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-20 Bridgeable extension for ERC-20 tokens." + "description": "ERC-20 Bridgeable extension for ERC-20 tokens.", + "slug": "/docs/contracts/token/ERC20/ERC20Bridgeable" } } diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json b/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json index 54093a31..05d8340d 100644 --- a/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json +++ b/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-20 Permit extension for ERC-20 tokens." + "description": "ERC-20 Permit extension for ERC-20 tokens.", + "slug": "/docs/contracts/token/ERC20/ERC20Permit" } } diff --git a/website/docs/contracts/token/ERC20/_category_.json b/website/docs/contracts/token/ERC20/_category_.json index 0b74f444..561dc476 100644 --- a/website/docs/contracts/token/ERC20/_category_.json +++ b/website/docs/contracts/token/ERC20/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-20 fungible token implementations." + "description": "ERC-20 fungible token implementations.", + "slug": "/docs/contracts/token/ERC20" } } diff --git a/website/docs/contracts/token/ERC6909/ERC6909/_category_.json b/website/docs/contracts/token/ERC6909/ERC6909/_category_.json index 5653b1ef..24639b1e 100644 --- a/website/docs/contracts/token/ERC6909/ERC6909/_category_.json +++ b/website/docs/contracts/token/ERC6909/ERC6909/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-6909 minimal multi-token implementations." + "description": "ERC-6909 minimal multi-token implementations.", + "slug": "/docs/contracts/token/ERC6909/ERC6909" } } diff --git a/website/docs/contracts/token/ERC6909/_category_.json b/website/docs/contracts/token/ERC6909/_category_.json index 5653b1ef..82d63b49 100644 --- a/website/docs/contracts/token/ERC6909/_category_.json +++ b/website/docs/contracts/token/ERC6909/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-6909 minimal multi-token implementations." + "description": "ERC-6909 minimal multi-token implementations.", + "slug": "/docs/contracts/token/ERC6909" } } diff --git a/website/docs/contracts/token/ERC721/ERC721/_category_.json b/website/docs/contracts/token/ERC721/ERC721/_category_.json index 5fdbd55a..267dcc9e 100644 --- a/website/docs/contracts/token/ERC721/ERC721/_category_.json +++ b/website/docs/contracts/token/ERC721/ERC721/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-721 non-fungible token implementations." + "description": "ERC-721 non-fungible token implementations.", + "slug": "/docs/contracts/token/ERC721/ERC721" } } diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json index 6ab22b34..8eb69a47 100644 --- a/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json +++ b/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-721 Enumerable extension for ERC-721 tokens." + "description": "ERC-721 Enumerable extension for ERC-721 tokens.", + "slug": "/docs/contracts/token/ERC721/ERC721Enumerable" } } diff --git a/website/docs/contracts/token/ERC721/_category_.json b/website/docs/contracts/token/ERC721/_category_.json index 5fdbd55a..8dc152ec 100644 --- a/website/docs/contracts/token/ERC721/_category_.json +++ b/website/docs/contracts/token/ERC721/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-721 non-fungible token implementations." + "description": "ERC-721 non-fungible token implementations.", + "slug": "/docs/contracts/token/ERC721" } } diff --git a/website/docs/contracts/token/Royalty/_category_.json b/website/docs/contracts/token/Royalty/_category_.json index 95a86a02..10ec448e 100644 --- a/website/docs/contracts/token/Royalty/_category_.json +++ b/website/docs/contracts/token/Royalty/_category_.json @@ -5,6 +5,7 @@ "collapsed": true, "link": { "type": "generated-index", - "description": "ERC-2981 royalty standard implementations." + "description": "ERC-2981 royalty standard implementations.", + "slug": "/docs/contracts/token/Royalty" } } diff --git a/website/docs/contracts/token/_category_.json b/website/docs/contracts/token/_category_.json index 4de378d1..f10e9138 100644 --- a/website/docs/contracts/token/_category_.json +++ b/website/docs/contracts/token/_category_.json @@ -5,6 +5,7 @@ "collapsed": false, "link": { "type": "generated-index", - "description": "Token standard implementations for Compose diamonds." + "description": "Token standard implementations for Compose diamonds.", + "slug": "/docs/contracts/token" } } diff --git a/website/docs/contribution/_category_.json b/website/docs/contribution/_category_.json index 42c2e348..03a61040 100644 --- a/website/docs/contribution/_category_.json +++ b/website/docs/contribution/_category_.json @@ -3,8 +3,9 @@ "position": 5, "link": { "type": "generated-index", - "description": "Learn how to contribute to Compose" + "description": "Learn how to contribute to Compose", + "slug": "/docs/contribution" }, "collapsible": true, "collapsed": true -} \ No newline at end of file +} diff --git a/website/docs/getting-started/_category_.json b/website/docs/getting-started/_category_.json index 74b10c34..f17bd463 100644 --- a/website/docs/getting-started/_category_.json +++ b/website/docs/getting-started/_category_.json @@ -3,9 +3,9 @@ "position": 3, "link": { "type": "generated-index", - "description": "Learn how to install and configure Compose for your smart contract projects." + "description": "Learn how to install and configure Compose for your smart contract projects.", + "slug": "/docs/getting-started" }, "collapsible": true, "collapsed": true } - diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json deleted file mode 100644 index 4af407fc..00000000 --- a/website/docs/library/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Library", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "title": "Library Reference", - "description": "API reference for all Compose modules and facets." - } -} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx deleted file mode 100644 index 2c5d71f4..00000000 --- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx +++ /dev/null @@ -1,566 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Manages roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages roles and permissions within a diamond. - - - -- Role-Based Access Control (RBAC) system. -- Supports granting and revoking roles to individual accounts or batches. -- Administered roles can be managed by designated admin roles. -- Provides functions to check and enforce role ownership. - - -## Overview - -The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular management of permissions by defining roles and assigning them to accounts. This facet enables administrators to grant, revoke, and renounce roles, ensuring that only authorized entities can perform specific actions. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlFacet} from "@compose/diamond/facets/AccessControl/IAccessControlFacet.sol"; -import {IDiamondCut} from "@compose/diamond/facets/DiamondCut/IDiamondCut.sol"; - -// Assume diamondCutFacet is the selector for the DiamondCutFacet -// Assume accessControlFacet is the selector for the AccessControlFacet - -contract Deployer { - address public diamondAddress; - - function deployDiamond(address _diamondAdmin) public { - // Deployment logic for the diamond proxy... - diamondAddress = address(0); // Placeholder for actual diamond address - } - - function grantAdminRole() public { - IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); - address _diamondAdmin = msg.sender; // Or any other admin address - bytes32 adminRole = keccak256("ADMIN_ROLE"); - - accessControl.grantRole(adminRole, _diamondAdmin); - } - - function revokeRoleFromUser(address _user) public { - IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); - bytes32 userRole = keccak256("USER_ROLE"); - - accessControl.revokeRole(userRole, _user); - } - - function hasUserRole(address _user) public view returns (bool) { - IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); - bytes32 userRole = keccak256("USER_ROLE"); - - return accessControl.hasRole(userRole, _user); - } - - function requireUserRole(address _user) public view { - IAccessControlFacet accessControl = IAccessControlFacet(diamondAddress); - bytes32 userRole = keccak256("USER_ROLE"); - - accessControl.requireRole(userRole, _user); - } -}`} - - -## Best Practices - - -- Grant roles only to trusted addresses. The role of the entity granting roles should itself be strictly controlled. -- Use `grantRoleBatch` and `revokeRoleBatch` for efficiency when managing multiple accounts for the same role. -- Define roles using `bytes32` hashes for clarity and to prevent accidental role collisions. - - -## Security Considerations - - -Ensure that the caller has the necessary administrative privileges before granting or revoking roles. Reentrancy is not a concern for role management functions as they do not make external calls. Input validation is handled by the underlying diamond proxy logic; however, ensure correct role identifiers are used. - - -
- -
- - diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx deleted file mode 100644 index e46e66bc..00000000 --- a/website/docs/library/access/AccessControl/AccessControlMod.mdx +++ /dev/null @@ -1,443 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Manage roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage roles and permissions within a diamond. - - - -- Role-based access control for diamond functions. -- Permission management for granting and revoking roles. -- Ability to set administrative roles for other roles. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides a robust Access Control system, enabling granular management of roles and permissions for accounts interacting with the diamond. It ensures that sensitive operations are restricted to authorized entities, enhancing the security and integrity of the diamond's functionalities. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControl} from "@compose/modules/access-control/IAccessControl.sol"; - -contract MyFacet { - IAccessControl immutable accessControl; - - constructor(address _accessControlAddress) { - accessControl = IAccessControl(_accessControlAddress); - } - - function grantAdminRole(address _account) external { - // Ensure the caller has the ADMIN_ROLE before granting it - accessControl.requireRole(IAccessControl.ADMIN_ROLE, msg.sender); - accessControl.grantRole(IAccessControl.ADMIN_ROLE, _account); - } - - function checkAdmin(address _account) external view returns (bool) { - return accessControl.hasRole(IAccessControl.ADMIN_ROLE, _account); - } -}`} - - -## Best Practices - - -- Use custom errors for role-based reverts for gas efficiency and clarity. -- Ensure that only authorized roles can manage other roles (e.g., ADMIN_ROLE manages all other roles). -- When revoking roles, always verify the caller's permissions. - - -## Integration Notes - - -The AccessControl module relies on its own storage slot within the diamond. Facets interact with this module by calling its external functions through the diamond proxy. Changes to role assignments are immediately visible to all facets upon the transaction's successful execution. The `AccessControlMod` stores role assignments and admin role configurations. When implementing new facets that require access control, call `hasRole` or `requireRole` to enforce permissions. - - -
- -
- - diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json deleted file mode 100644 index 25db9246..00000000 --- a/website/docs/library/access/AccessControl/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Access Control", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Role-based access control (RBAC) pattern." - } -} diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx deleted file mode 100644 index 9a4e3353..00000000 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,373 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Manage role pausing and access control within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage role pausing and access control within a diamond. - - - -- Temporarily disable specific roles using `pauseRole`. -- Re-enable paused roles with `unpauseRole`. -- Check role pause status via `isRolePaused`. -- Integrates seamlessly with the diamond's existing access control mechanisms. - - -## Overview - -This facet provides granular control over role permissions by allowing specific roles to be temporarily paused. It integrates with the diamond's access control system, ensuring that paused roles cannot be invoked. This enables robust operational management and emergency halt capabilities for critical functions. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose-protocol/diamond-contracts/facets/DiamondLoupe.sol"; -import {AccessControlPausableFacet} from "@compose-protocol/diamond-contracts/facets/AccessControlPausable.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/interfaces/IDiamondCut.sol"; - -contract Deployer { - address immutable DIAMOND_ADDRESS; - - constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; - } - - function pauseRole(bytes4 roleSelector) external { - AccessControlPausableFacet(DIAMOND_ADDRESS).pauseRole(roleSelector); - } - - function unpauseRole(bytes4 roleSelector) external { - AccessControlPausableFacet(DIAMOND_ADDRESS).unpauseRole(roleSelector); - } - - function isRolePaused(bytes4 roleSelector) external view returns (bool) { - return AccessControlPausableFacet(DIAMOND_ADDRESS).isRolePaused(roleSelector); - } -}`} - - -## Best Practices - - -- Initialize roles and grant permissions before pausing or unpausing them. -- Use `pauseRole` for temporary operational halts and `unpauseRole` to resume functionality. -- Ensure the caller has the appropriate administrative role to pause or unpause a specific role. - - -## Security Considerations - - -Access to `pauseRole` and `unpauseRole` is restricted to the admin of the specific role, preventing unauthorized pausing or unpausing. The `requireRoleNotPaused` internal function ensures that calls to paused roles are reverted, preventing unintended execution. Ensure that role administration is correctly configured to maintain security. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx deleted file mode 100644 index 94cac87a..00000000 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx +++ /dev/null @@ -1,381 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Manages role-based access control with pause functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages role-based access control with pause functionality. - - - -- Role-based access control enforcement. -- Ability to pause and unpause specific roles, halting associated operations. -- Reverts with specific errors for unauthorized access or paused roles. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module integrates role-based access control with the ability to pause specific roles. It allows for granular control over who can perform actions and provides a mechanism to temporarily halt operations for a role during critical events or maintenance. This ensures operational safety and administrative flexibility within a Compose diamond. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausableMod} from "@compose/diamond/modules/access-control/pausable/IAccessControlPausableMod.sol"; - -contract MyFacet { - IAccessControlPausableMod internal accessControlPausableMod; - - constructor(address _accessControlPausableMod) { - accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); - } - - function someProtectedFunction() external { - // Assuming 'MY_ROLE' is a defined role ID - address caller = msg.sender; - uint256 roleId = 1; // Example Role ID - accessControlPausableMod.requireRoleNotPaused(caller, roleId); - - // Proceed with function logic - } - - function pauseMyRole() external { - uint256 roleId = 1; // Example Role ID - accessControlPausableMod.pauseRole(roleId); - } - - function unpauseMyRole() external { - uint256 roleId = 1; // Example Role ID - accessControlPausableMod.unpauseRole(roleId); - } -}`} - - -## Best Practices - - -- Use `requireRoleNotPaused` to enforce both role membership and operational status before executing sensitive logic. -- Implement separate administrative functions to call `pauseRole` and `unpauseRole`, restricting access to authorized roles or the owner. -- Handle `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` errors explicitly in calling facets to provide clear user feedback. - - -## Integration Notes - - -This module manages its state within its own storage slots. Facets interact with it via its interface. The `requireRoleNotPaused` function checks both role presence and the pause status of that role. Changes to role pause states are immediately visible to all facets interacting with the module. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json deleted file mode 100644 index ab207a3c..00000000 --- a/website/docs/library/access/AccessControlPausable/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Pausable Access Control", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "RBAC with pause functionality." - } -} diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx deleted file mode 100644 index 8e829b4c..00000000 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,446 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments in Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages time-bound role assignments in Compose diamonds. - - - -- Grants roles with specific expiry timestamps. -- Provides a mechanism to check if a role assignment has expired. -- Enforces role validity before execution of sensitive operations. - - -## Overview - -The AccessControlTemporalFacet extends Compose's access control system by introducing time-bound role assignments. This facet allows administrators to grant roles that automatically expire, enhancing granular control over permissions within a diamond. It provides functions to manage these temporal roles and check their validity. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IComposeDiamond} from "@compose/diamond/contracts/interfaces/IComposeDiamond.sol"; -import {AccessControlTemporalFacet} from "@compose/access-control/contracts/AccessControlTemporalFacet.sol"; - -contract DeployExample { - IComposeDiamond immutable diamondProxy; - - constructor(address _diamondProxy) { - diamondProxy = IComposeDiamond(_diamondProxy); - } - - function grantRoleWithExpiry(bytes32 _role, address _account, uint64 _expiry) external { - address facetAddress = diamondProxy.facetAddress(AccessControlTemporalFacet.FACET_FUNCTION_SELECTORS.grantRoleWithExpiry); - AccessControlTemporalFacet(facetAddress).grantRoleWithExpiry(_role, _account, _expiry); - } - - function isRoleExpired(bytes32 _role, address _account) external view returns (bool) { - address facetAddress = diamondProxy.facetAddress(AccessControlTemporalFacet.FACET_FUNCTION_SELECTORS.isRoleExpired); - return AccessControlTemporalFacet(facetAddress).isRoleExpired(_role, _account); - } -}`} - - -## Best Practices - - -- Grant temporal roles only to trusted administrators who understand the expiry implications. -- Regularly audit expiring roles to ensure continued necessity or timely renewal. -- Use `requireValidRole` in critical functions to enforce active role assignments. - - -## Security Considerations - - -Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the admin of the respective role, preventing unauthorized role management. The `requireValidRole` function prevents the use of expired roles by reverting with `AccessControlRoleExpired`. Ensure the `admin` role itself is securely managed. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx deleted file mode 100644 index 03f3ca2c..00000000 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,479 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Manages role assignments with time-based expiry." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages role assignments with time-based expiry. - - - -- Grants roles with a configurable expiry timestamp. -- Automatically checks for role expiry, preventing access to expired roles. -- Provides explicit functions to check role validity and revoke temporal roles. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module extends role-based access control by introducing time-bound role assignments. It allows for roles to be granted with a specific expiry timestamp, enhancing security and operational flexibility. By managing temporal roles, diamonds can enforce time-sensitive permissions, automating the revocation of access when it's no longer needed. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {AccessControlTemporalMod} from "@compose/contracts/modules/AccessControlTemporalMod.sol"; -import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; - -contract MyFacet { - AccessControlTemporalMod internal acTemporal; - - function initialize(address diamondAdmin) { - // Assume AccessControlTemporalMod facet is deployed and its address is known - address acTemporalAddress = address(0x123); // Replace with actual facet address - acTemporal = AccessControlTemporalMod(acTemporalAddress); - } - - function grantAdminRoleWithExpiry(address user, uint64 expiry) external { - // Assuming the facet has access to the diamond's admin role - bytes32 adminRole = keccak256("DIAMOND_ADMIN_ROLE"); - acTemporal.grantRoleWithExpiry(adminRole, user, expiry); - } - - function checkAdminStatus(address user) external view { - bytes32 adminRole = keccak256("DIAMOND_ADMIN_ROLE"); - acTemporal.requireValidRole(adminRole, user); - } -}`} - - -## Best Practices - - -- Use `requireValidRole` in external functions to enforce time-bound access control before executing sensitive operations. -- Ensure the `expiry` timestamp passed to `grantRoleWithExpiry` is in Unix time (seconds since epoch). -- Regularly audit temporal role assignments and consider automated mechanisms for role cleanup. - - -## Integration Notes - - -This module utilizes the standard Compose diamond storage pattern. Facets interacting with this module will need to access its storage via the diamond proxy. The `AccessControlTemporalMod` facet manages its own internal storage for role expiry information, which is distinct from the core Access Control storage. Any facet that needs to check or manage temporal roles must be aware of this module's presence and call its functions directly. The `requireValidRole` function is designed to be called by other facets to enforce access control checks, reverting with specific errors if the role is unauthorized or expired. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json deleted file mode 100644 index 72012bb3..00000000 --- a/website/docs/library/access/AccessControlTemporal/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Temporal Access Control", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Time-limited role-based access control." - } -} diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx deleted file mode 100644 index 688cdec1..00000000 --- a/website/docs/library/access/Owner/OwnerFacet.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "Manages diamond ownership and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages diamond ownership and transfers. - - - -- Permissioned ownership control for administrative functions. -- Supports safe transfer of ownership to prevent accidental loss of control. -- Allows for complete renouncement of ownership. - - -## Overview - -The OwnerFacet provides essential functionality for managing the ownership of a Compose diamond. It allows for querying the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative actions within the diamond. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; - -contract OwnerCaller { - IOwnerFacet private immutable _ownerFacet; - - constructor(address _diamondAddress) { - _ownerFacet = IOwnerFacet(_diamondAddress); - } - - function callOwner() external { - address currentOwner = _ownerOwner(); - // Example: Transfer ownership if allowed - // _ownerFacet.transferOwnership(newOwner); - } - - function _ownerOwner() internal view returns (address) { - return _ownerFacet.owner(); - } -}`} - - -## Best Practices - - -- Ensure the OwnerFacet is initialized with the correct owner address during diamond deployment. -- Use `transferOwnership` to delegate administrative control safely. -- Consider the implications of `renounceOwnership` as it makes the diamond unmanageable by any single address. - - -## Security Considerations - - -Access control is paramount. Only the current owner can execute `transferOwnership` and `renounceOwnership`. Ensure that the address performing these actions is indeed the rightful owner to prevent unauthorized control of the diamond. Input validation is handled by the `transferOwnership` function, which reverts if `_newOwner` is the zero address, except when explicitly intended for renouncement. - - -
- -
- - diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx deleted file mode 100644 index 925a57ab..00000000 --- a/website/docs/library/access/Owner/OwnerMod.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "Manages ERC-173 contract ownership." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-173 contract ownership. - - - -- Implements ERC-173 ownership standard. -- Provides `owner()` view function to retrieve the current owner's address. -- Includes `requireOwner()` for easy access control enforcement. -- Supports renouncing ownership by setting the new owner to `address(0)`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod module provides essential functionality for managing contract ownership according to the ERC-173 standard. It ensures that only the designated owner can perform critical administrative actions, enhancing security and control within the diamond. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "./interfaces/IOwnerMod.sol"; - -contract MyOwnerFacet { - // Assumes OwnerMod is deployed at the correct STORAGE_POSITION - IOwnerMod public ownerMod; - - function transferMyContractOwnership(address _newOwner) external { - // Call the transferOwnership function from the OwnerMod - ownerMod.transferOwnership(_newOwner); - } - - function getMyContractOwner() external view returns (address) { - // Call the owner function from the OwnerMod - return ownerMod.owner(); - } - - function onlyOwnerAction() internal view { - // Use the requireOwner check from OwnerMod - ownerMod.requireOwner(); - // ... owner-only logic ... - } -}`} - - -## Best Practices - - -- Grant ownership only to trusted addresses. Use `transferOwnership` carefully, especially when renouncing ownership. -- Implement `requireOwner` checks before critical administrative functions to enforce access control. -- Be aware that changing ownership is a state-altering operation and should be handled with care during upgrades. - - -## Integration Notes - - -The OwnerMod stores ownership data in a dedicated storage slot, typically defined by `STORAGE_POSITION`. Facets that depend on ownership checks or need to manage ownership should import the `IOwnerMod` interface and interact with the module's functions. The `getStorage` function can be used to retrieve a pointer to the internal storage struct, allowing for direct access if necessary, though calling the provided functions is recommended for maintaining module integrity. - - -
- -
- - diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json deleted file mode 100644 index 274b507b..00000000 --- a/website/docs/library/access/Owner/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Owner", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Single-owner access control pattern." - } -} diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx deleted file mode 100644 index 5438a32d..00000000 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,292 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "Manage diamond ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond ownership with a two-step transfer process. - - - -- Two-step ownership transfer prevents accidental changes. -- Provides functions to query current and pending ownership. -- Supports `renounceOwnership` for relinquishing control. - - -## Overview - -This facet provides a robust, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are intentional and secure by requiring both the current owner to initiate a transfer and the new owner to accept it, preventing accidental or unauthorized takeovers. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "@compose/facets/owner/IOwnerTwoStepsFacet.sol"; - -contract DiamondOwnerManager { - IOwnerTwoStepsFacet public ownerFacet; - - constructor(address _ownerFacetAddress) { - ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); - } - - function initiateOwnershipTransfer(address _newOwner) external { - // Assuming the caller is the current owner - ownerFacet.transferOwnership(_newOwner); - } - - function acceptNewOwnership() external { - // Assuming the caller is the pending owner - ownerFacet.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function getPendingOwner() external view returns (address) { - return ownerFacet.pendingOwner(); - } -}`} - - -## Best Practices - - -- Initialize the facet with the current owner's address during diamond deployment. -- Ensure that only the current owner can call `transferOwnership` and only the pending owner can call `acceptOwnership`. -- Use `owner()` and `pendingOwner()` to monitor ownership status. - - -## Security Considerations - - -Access control is critical: `transferOwnership` must be callable only by the current owner, and `acceptOwnership` only by the pending owner. Failure to enforce this can lead to unauthorized ownership changes. The facet itself does not manage reentrancy; dependent facets or the diamond proxy must handle this. - - -
- -
- - diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx deleted file mode 100644 index 6b33932c..00000000 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "Two-step ownership transfer for secure contract management." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Two-step ownership transfer for secure contract management. - - - -- Secure two-step ownership transfer to prevent accidental loss. -- Explicit `transferOwnership` and `acceptOwnership` functions. -- `renounceOwnership` function to relinquish control. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module implements a secure two-step ownership transfer mechanism, preventing accidental ownership loss. It ensures that ownership changes are confirmed by both the current and pending owners, enhancing the safety of critical contract administration functions. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoSteps} from "./interfaces/IOwnerTwoSteps.sol"; - -contract MyOwnerFacet { - IOwnerTwoSteps private immutable _ownerTwoSteps; - - constructor(address ownerTwoStepsAddress) { - _ownerTwoSteps = IOwnerTwoSteps(ownerTwoStepsAddress); - } - - function transferContractOwnership(address _newOwner) external { - _ownerTwoSteps.transferOwnership(_newOwner); - } - - function acceptContractOwnership() external { - _ownerTwoSteps.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return _ownerTwoSteps.owner(); - } -}`} - - -## Best Practices - - -- Use `transferOwnership` only when initiating a change. The recipient must then call `acceptOwnership`. -- Call `renounceOwnership` with extreme caution, as it renders all owner-restricted functions unusable. -- Always check the return value of `owner()` and `pendingOwner()` to understand the current and intended ownership status. - - -## Integration Notes - - -The `OwnerTwoStepsMod` module manages ownership state in dedicated storage slots. Facets integrating this module should reference these slots using inline assembly as demonstrated in `getOwnerStorage` and `getPendingOwnerStorage`. The module enforces ownership checks via the `requireOwner` function, which must be called by any facet function requiring owner privileges. Ensure the `OwnerTwoStepsMod` facet is added to the diamond before any other facets that rely on its ownership management. - - -
- -
- - diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json deleted file mode 100644 index 52fea0d0..00000000 --- a/website/docs/library/access/OwnerTwoSteps/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Two-Step Owner", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Two-step ownership transfer pattern." - } -} diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json deleted file mode 100644 index 3d29d95a..00000000 --- a/website/docs/library/access/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Access Control", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Access control patterns for permission management in Compose diamonds." - } -} diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx deleted file mode 100644 index 3faae578..00000000 --- a/website/docs/library/diamond/DiamondCutFacet.mdx +++ /dev/null @@ -1,419 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Manage diamond facets and functions" -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and functions - - - -- Allows adding, replacing, and removing facets and their functions. -- Supports executing an initialization function call after a diamond cut. -- Provides functions to inspect diamond storage and facet ownership. - - -## Overview - -The DiamondCutFacet provides essential functions for managing the diamond proxy's facets and their associated functions. It allows authorized users to add, replace, or remove facets and functions, enabling dynamic upgrades and modifications of the diamond's functionality. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutFacet} from "@compose/diamond-contracts/facets/DiamondCutFacet.sol"; -import {IDiamondCut} from "@compose/diamond-contracts/interfaces/IDiamondCut.sol"; - -contract MyDiamond is IDiamondCut { - // ... other facets and storage - - function upgradeDiamond() external { - address diamondCutFacetAddress = address(this); // Assume deployed and accessible - bytes32[] memory selectors = new bytes32[](1); - selectors[0] = IDiamondCut.diamondCut.selector; - - // Example: Adding a new facet - // diamondCut(newFacetCuts, initialFacets, initAddress, initCalldata) - diamondCut( - new IDiamondCut.FacetCut[](0), // No facets to add initially for this example - new IDiamondCut.FacetCut[](1) {{ - facetAddress = address(new NewFacet()); // Address of the new facet contract - action = IDiamondCut.FacetCutAction.ADD; - isPure = false; // or true if the facet only contains view functions - functionSelectors = selectors; // Selectors to be added - }}, - address(0), // No initialization contract - '()' // Empty initialization calldata - ); - } - - // ... -}`} - - -## Best Practices - - -- Only the diamond owner or an authorized address should call `diamondCut` functions. -- Ensure facet addresses are verified before adding or replacing to prevent malicious code injection. -- Carefully manage function selector mappings to avoid conflicts and ensure correct dispatch. - - -## Security Considerations - - -The `diamondCut` function is highly sensitive and must be protected by robust access control. Incorrectly adding or replacing facets can lead to unexpected behavior or rug pulls. Ensure all facet code is audited before deployment. The `removeFunctions` and `removeFacet` actions require careful consideration to avoid bricking the diamond. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx deleted file mode 100644 index 94479b4e..00000000 --- a/website/docs/library/diamond/DiamondCutMod.mdx +++ /dev/null @@ -1,393 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Manages diamond facet additions, replacements, and removals." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages diamond facet additions, replacements, and removals. - - - -- Atomic facet updates: Add, replace, or remove multiple facets in a single transaction. -- Optional initialization: Supports executing an initialization function during the cut process. -- Immutable function protection: Prevents accidental removal or replacement of critical immutable functions. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCutMod provides the core functionality for managing facets within a Compose diamond. It enables atomic updates to the diamond's interface by adding, replacing, or removing functions. This module is crucial for upgrading and extending diamond capabilities in a controlled and gas-efficient manner. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-proxy/contracts/modules/diamondCut/IDiamondCut.sol"; -import {DiamondCutMod} from "@compose/diamond-proxy/contracts/modules/diamondCut/DiamondCutMod.sol"; - -contract MyDiamond is IDiamondCut { - address constant DIAMOND_CUT_MODULE = address(new DiamondCutMod()); - - function diamondCut(FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) external override { - // Delegate to the DiamondCutMod to perform the cut - IDiamondCut(DIAMOND_CUT_MODULE).diamondCut(_diamondCut, _init, _calldata); - } - - // Other diamond functions and selectors... -} - -// Example of calling diamondCut from another facet: -contract FacetManager { - IDiamondCut public diamondProxy; - - constructor(address _diamondProxy) { - diamondProxy = IDiamondCut(_diamondProxy); - } - - function updateFacet(bytes4[] memory _selectors, address _newFacetAddress, address _facetAddressToReplace) public { - // Assuming DiamondCutMod is deployed at a known address or accessible via the diamond proxy itself - // For simplicity, assume diamondProxy is the interface to the diamond's cut functionality - FacetCut[] memory cuts = new FacetCut[](1); - cuts[0] = FacetCut({ - facetAddress: _newFacetAddress, - action: 2, // REPLACE - selectors: _selectors - }); - - // In a real scenario, you'd need to know the correct init address and calldata if applicable - diamondProxy.diamondCut(cuts, address(0), ""); - } -}`} - - -## Best Practices - - -- Use `diamondCut` for all facet modifications to ensure atomicity and proper state transitions. -- Carefully review selectors and facet addresses before execution to prevent unintended function loss or overwrites. -- Handle errors such as `CannotAddFunctionToDiamondThatAlreadyExists` and `CannotRemoveImmutableFunction` to ensure robust upgrade logic. - - -## Integration Notes - - -The DiamondCutMod stores facet information and selectors within the diamond's storage. It uses a mapping of selectors to facet addresses. Any changes made via `diamondCut` are immediately reflected in the diamond proxy's dispatch mechanism. Facets that interact with functions managed by DiamondCutMod should be aware that function addresses can change during upgrades. The order of operations within a single `diamondCut` call matters for replacements and additions. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx deleted file mode 100644 index b52f59f9..00000000 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,250 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "Inspect diamond facets, selectors, and storage." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Inspect diamond facets, selectors, and storage. - - - -- Provides detailed introspection of diamond facets and their associated function selectors. -- Offers efficient querying of facet addresses and function mappings using optimized internal logic. -- Enables retrieval of all deployed facets and their selectors in a single call. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, the functions they implement, and the addresses they are mapped to. This facet is crucial for understanding the diamond's surface area and for debugging. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondLoupe} from "./IDiamondLoupe.sol"; - -contract MyDiamond { - IDiamondLoupe constant diamondLoupe = IDiamondLoupe(address(this)); - - function getFacetAddresses() external view returns (address[] memory) { - return diamondLoupe.facetAddresses(); - } - - function getFacetSelectors(address _facet) external view returns (bytes4[] memory) { - return diamondLoupe.facetFunctionSelectors(_facet); - } - - function getAllFacets() external view returns (Facet[] memory) { - return diamondLoupe.facets(); - } -}`} - - -## Best Practices - - -- Deploy this facet as part of the initial diamond setup or upgrade process. -- Use the functions provided for debugging and understanding the diamond's composition, rather than for core application logic. -- Ensure the diamond's internal storage (`DiamondStorage`) is accessible to this facet for accurate data retrieval. - - -## Security Considerations - - -This facet is read-only and does not modify state, making it inherently safe from reentrancy attacks. Access control is typically managed at the diamond proxy level; ensure that access to these introspection functions is appropriately restricted if sensitive information about the diamond's structure needs to be protected. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx deleted file mode 100644 index 83101a6a..00000000 --- a/website/docs/library/diamond/DiamondMod.mdx +++ /dev/null @@ -1,238 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Internal functions and storage for diamond proxy." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Internal functions and storage for diamond proxy. - - - -- Manages facet registration and function selector mapping during diamond deployment. -- Provides the core fallback mechanism for routing external calls to registered facets. -- Exposes diamond's internal storage for inspection. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondMod module manages facet additions and provides core proxy logic for function dispatch. It ensures that facets are correctly registered and that function calls are routed to the appropriate facet implementation, forming the bedrock of diamond composability. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import { IDiamondMod } from "@compose/diamond-proxy/src/modules/diamondMod/IDiamondMod.sol"; - -contract MyFacet { - IDiamondMod public diamondMod; - - function initialize(IDiamondMod _diamondMod) external { - diamondMod = _diamondMod; - } - - function addMyFacet() external { - // This function would typically be called during diamond deployment. - // For demonstration, assuming diamondMod is already initialized. - // In a real scenario, this logic resides in the diamond deployer. - // diamondMod.addFacets(...); // Not directly callable by external facets post-deployment. - } - - function getDiamondStorage() external view returns (bytes memory) { - return diamondMod.getStorage(); - } -}`} - - -## Best Practices - - -- Facet additions (`addFacets`) are restricted to the diamond deployment phase to maintain proxy integrity and prevent runtime manipulation. -- Utilize `diamondFallback` for routing external calls to the correct facet implementation; ensure all necessary function selectors are registered. -- Access diamond storage via `getStorage` for read-only operations to understand the proxy's current state. - - -## Integration Notes - - -DiamondMod stores facet addresses and their associated function selectors. The `addFacets` function is intended for use only during the initial deployment of the diamond, as adding facets dynamically after deployment is not supported by this module. The `diamondFallback` function acts as the central dispatcher, inspecting the called function selector and forwarding the call to the corresponding facet's implementation. `getStorage` provides a snapshot of the diamond's internal storage layout, which can be crucial for understanding facet interactions and state. - - -
- -
- - diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json deleted file mode 100644 index 8b248747..00000000 --- a/website/docs/library/diamond/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Diamond Core", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Core diamond proxy functionality for ERC-2535 diamonds." - } -} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx deleted file mode 100644 index 5c3236f1..00000000 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ /dev/null @@ -1,150 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Example Diamond initialization and routing" -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Example Diamond initialization and routing - - - -- Manages initial facet registration and diamond ownership. -- Supports adding facets and their associated function selectors. -- Enables delegatecall routing by mapping function selectors to facet addresses. - - -## Overview - -The ExampleDiamond facet serves as a foundational component for Compose diamonds, demonstrating core initialization and routing mechanisms. It handles the initial setup of facets and establishes the diamond's owner, enabling delegatecall routing for all registered functions. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose-protocol/diamond-sdk/src/interfaces/IDiamondCut.sol"; -import {IDiamondLoupe} from "@compose-protocol/diamond-sdk/src/interfaces/IDiamondLoupe.sol"; -import {ExampleDiamond} from "@compose-protocol/diamond-sdk/src/facets/ExampleDiamond.sol"; - -contract DeployExampleDiamond is IDiamondCut { - address public diamondProxy; - - function deploy() public { - // Example facet addresses and selectors (replace with actual deployment) - address facet1Address = address(0x123); - bytes4[] memory facet1Selectors = new bytes4[](1); - facet1Selectors[0] = ExampleDiamond.someFunction.selector; - - address facet2Address = address(0x456); - bytes4[] memory facet2Selectors = new bytes4[](1); - facet2Selectors[0] = ExampleDiamond.anotherFunction.selector; - - // Construct FacetCut data - FacetCut[] memory cuts = new FacetCut[](2); - cuts[0] = FacetCut({ - facetAddress: facet1Address, - action: FacetCutAction.Add, - functionSelectors: facet1Selectors - }); - cuts[1] = FacetCut({ - facetAddress: facet2Address, - action: FacetCutAction.Add, - functionSelectors: facet2Selectors - }); - - // Deploy Diamond Proxy and initialize with facets and owner - // Assume ExampleDiamond is the contract implementing the constructor logic for initialization - // In a real scenario, you would deploy a DiamondProxy contract and call its diamondCut function - // This example simplifies by directly calling a conceptual constructor-like initializer - // diamondProxy = address(new DiamondProxy(cuts, msg.sender)); // Conceptual deployment - - // For demonstration, assume ExampleDiamond itself is being deployed with initialization logic - // In practice, this would be a separate DiamondProxy contract - // diamondProxy = address(new ExampleDiamond(cuts, msg.sender)); // Conceptual deployment - } -} -`} - - -## Best Practices - - -- Initialize the diamond with all necessary facets and their function selectors during deployment. -- Ensure the owner is set correctly to manage diamond upgrades and facet additions. -- The `fallback` and `receive` functions should be handled by the diamond proxy itself, not typically within this facet. - - -## Security Considerations - - -Access control for ownership and facet management should be robust, typically managed by an `Ownable` pattern or a similar access control mechanism. Ensure that facet addresses provided during initialization are trusted and verified to prevent malicious code injection. Input validation on `FacetCut` data is crucial to prevent errors during initialization. - - -
- -
- - diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json deleted file mode 100644 index d94c8663..00000000 --- a/website/docs/library/diamond/example/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "example", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "example components for Compose diamonds." - } -} diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx deleted file mode 100644 index e729d865..00000000 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "ERC-165 interface detection and registration." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-165 interface detection and registration. - - - -- Implements the ERC-165 standard for interface detection. -- Manages a persistent registry of supported interfaces within diamond storage. -- Provides internal helper functions for facets to register and query interfaces. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod provides the standard ERC-165 interface detection mechanism for Compose diamonds. It manages the storage for supported interfaces and exposes functions to register new interfaces and retrieve the storage pointer, enabling facets to correctly implement the ERC-165 standard. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC165, ERC165Mod} from "@compose-protocol/diamond-contracts/modules/erc165/ERC165Mod.sol"; - -contract MyFacet { - function initialize() external { - // Register ERC721 interface during facet initialization - ERC165Mod.registerInterface(type(IERC721).interfaceId); - } - - function supportsInterface(bytes4 interfaceId) external view returns (bool) { - return ERC165Mod.supportsInterface(interfaceId); - } -}`} - - -## Best Practices - - -- Register all supported interfaces during facet initialization to ensure correct ERC-165 compliance. -- Call `ERC165Mod.supportsInterface(interfaceId)` within your facet's `supportsInterface` implementation for standard ERC-165 behavior. -- Ensure the `ERC165Mod` is correctly initialized and its storage is accessible. - - -## Integration Notes - - -The ERC165Mod utilizes a dedicated storage slot for its `ERC165Storage` struct. Facets interact with this storage via the `getStorage` internal function, which uses inline assembly to bind to the correct storage position. All facets should call `ERC165Mod.registerInterface` during their initialization to declare supported interfaces. The `supportsInterface` function on the diamond proxy will delegate to the ERC165Mod implementation. - - -
- -
- - diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json deleted file mode 100644 index 2f19715b..00000000 --- a/website/docs/library/interfaceDetection/ERC165/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-165", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-165 components for Compose diamonds." - } -} diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json deleted file mode 100644 index 62741de4..00000000 --- a/website/docs/library/interfaceDetection/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Interface Detection", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-165 interface detection support." - } -} diff --git a/website/docs/library/libraries/NonReentrancyMod.mdx b/website/docs/library/libraries/NonReentrancyMod.mdx deleted file mode 100644 index 98c6e151..00000000 --- a/website/docs/library/libraries/NonReentrancyMod.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "Prevent reentrant calls within diamond functions." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Prevent reentrant calls within diamond functions. - - - -- Prevents reentrant function calls by tracking execution state. -- Uses `enter()` to acquire a lock and `exit()` to release it. -- Emits a specific `Reentrancy` error upon detecting a reentrant attempt. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod provides essential mechanisms to prevent reentrant function calls. By implementing the `enter` and `exit` functions, facets can enforce that a function's execution is not interrupted by another call to the same function (or another function protected by the same mechanism) before it completes. This is critical for maintaining state integrity and security in complex smart contract interactions. - ---- - -## Storage - -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {NonReentrancyMod} from "@compose/modules/NonReentrancyMod.sol"; - -contract MyFacet { - NonReentrancyMod internal nonReentrancyMod; - - constructor(address _diamondProxy) { - nonReentrancyMod = NonReentrancyMod(_diamondProxy); - } - - function sensitiveOperation() external { - // Check if reentrancy is already active - if (!nonReentrancyMod.enter()) { - // ReentrancyGuard is already active, revert with Reentrancy error - revert NonReentrancyMod.Reentrancy(); - } - - // ... perform sensitive operations ... - - // Exit the ReentrancyGuard - nonReentrancyMod.exit(); - } -}`} - - -## Best Practices - - -- Always pair `enter()` calls with corresponding `exit()` calls to prevent deadlocks. -- Use the `NonReentrancyMod.Reentrancy()` custom error for clear revert reasons. -- Ensure `enter()` is called at the beginning and `exit()` at the end of sensitive operations. - - -## Integration Notes - - -NonReentrancyMod relies on a single storage slot within the diamond's storage layout to maintain its reentrancy lock status. Facets integrating this module must ensure that the `NonReentrancyMod` library is correctly initialized and that its `enter` and `exit` functions are called in the correct sequence. The state managed by this module is internal to the library and does not directly expose storage variables to other facets. - - -
- -
- - diff --git a/website/docs/library/libraries/_category_.json b/website/docs/library/libraries/_category_.json deleted file mode 100644 index f20ad00a..00000000 --- a/website/docs/library/libraries/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Utilities", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Utility libraries and helpers for diamond development." - } -} diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx deleted file mode 100644 index 447fae21..00000000 --- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx +++ /dev/null @@ -1,666 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "Manages ERC-1155 multi-token standard functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 multi-token standard functionality. - - - -- Supports both single and batch token transfers (`safeTransferFrom`, `safeBatchTransferFrom`). -- Provides efficient balance checking via `balanceOf` and `balanceOfBatch`. -- Manages operator approvals for token management through `setApprovalForAll` and `isApprovedForAll`. - - -## Overview - -The ERC1155Facet provides a comprehensive implementation of the ERC-1155 standard for multi-token management within a Compose diamond. It enables tracking token balances, managing approvals, and performing token transfers efficiently through batched operations. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC1155/IERC1155Facet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; - -contract ERC1155Deployer { - address diamondAddress; - - function deploy() public { - // Assume diamondAddress is already set or initialized - IERC1155Facet erc1155Facet = IERC1155Facet(diamondAddress); - - // Example: Get balance of token ID 1 for address(this) - uint256 balance = erc1155Facet.balanceOf(address(this), 1); - - // Example: Set approval for operator - erc1155Facet.setApprovalForAll(address(0xOperator), true); - - // Example: Get URI for token ID 1 - string memory tokenUri = erc1155Facet.uri(1); - } -}`} - - -## Best Practices - - -- Initialize the `baseURI` and `tokenURIs` using the `setURI` function if token-specific URIs are required. -- Ensure necessary approvals are set via `setApprovalForAll` before attempting transfers on behalf of other accounts. -- Leverage `balanceOfBatch` and `safeBatchTransferFrom` for gas efficiency when dealing with multiple token types or accounts. - - -## Security Considerations - - -Access control for minting and burning is managed by separate facets. Ensure that the `safeTransferFrom` and `safeBatchTransferFrom` functions are called with valid sender and receiver addresses. The ERC1155InvalidSender, ERC1155InvalidReceiver, and ERC1155MissingApprovalForAll errors help prevent common transfer issues. - - -
- -
- - diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx deleted file mode 100644 index 9b483079..00000000 --- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx +++ /dev/null @@ -1,607 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "Manages ERC-1155 token minting, burning, and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 token minting, burning, and transfers. - - - -- Supports both single and batch minting and burning operations. -- Implements safe transfer logic, including ERC1155Receiver validation for contract recipients. -- Provides functionality to set base and token-specific URIs for metadata. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module implements the core ERC-1155 token logic, enabling minting, burning, and safe transfers of multiple token types. It adheres to EIP-1155 standards, ensuring secure handling of token balances and receiver interactions. Integrating this module allows diamonds to function as robust ERC-1155 token issuers and managers. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Mod} from "@compose/modules/ERC1155Mod.sol"; - -contract MyDiamondFacet { - IERC1155Mod internal constant ERC1155 = IERC1155Mod(address(this)); - - function mintMyTokens(address to, uint256 id, uint256 amount) external { - ERC1155.mint(to, id, amount); - } - - function burnMyTokens(address from, uint256 id, uint256 amount) external { - ERC1155.burn(from, id, amount); - } - - function safeTransferMyTokens( - address from, - address to, - uint256 id, - uint256 amount, - bytes calldata data - ) external { - ERC1155.safeTransferFrom(from, to, id, amount, data); - } -}`} - - -## Best Practices - - -- Always validate `to` and `from` addresses before performing transfers or burns to prevent unintended state changes. -- Ensure the caller has sufficient approval or ownership before executing `safeTransferFrom` or `safeBatchTransferFrom`. -- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, and `ERC1155InvalidReceiver` errors gracefully in consuming facets. - - -## Integration Notes - - -The ERC1155Mod module interacts with a predefined storage slot within the diamond's storage layout to manage token balances, approvals, and URIs. Facets integrating this module will access this shared storage. The `getStorage` function provides direct access to this storage struct. Ensure that no other facets modify the relevant storage variables in ways that would violate ERC-1155 invariants. - - -
- -
- - diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json deleted file mode 100644 index 012a98bd..00000000 --- a/website/docs/library/token/ERC1155/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-1155", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-1155 multi-token implementations." - } -} diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx deleted file mode 100644 index a0b36f15..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ /dev/null @@ -1,247 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "Burn ERC-20 tokens within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC-20 tokens within a diamond. - - - -- Supports burning tokens directly from the caller's balance. -- Enables burning tokens from other accounts via allowance. -- Adheres to ERC-20 standard by emitting `Transfer` events to the zero address. - - -## Overview - -The ERC20BurnFacet allows for the destruction of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account using their allowance, ensuring compliance with ERC-20 standards by emitting Transfer events to the zero address. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20BurnFacet} from "@compose/contracts/src/facets/ERC20/ERC20BurnFacet.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -contract ExampleDiamond { - address constant ERC20_BURN_FACET_ADDRESS = address(0x...); // Address of the deployed ERC20BurnFacet - - function burnMyTokens(address tokenAddress, uint256 amount) external { - ERC20BurnFacet(ERC20_BURN_FACET_ADDRESS).burn(tokenAddress, amount); - } - - function burnTokensFrom(address tokenAddress, address from, uint256 amount) external { - // Ensure allowance is set before calling burnFrom - // IERC20(tokenAddress).approve(address(this), allowance); // Example approval - ERC20BurnFacet(ERC20_BURN_FACET_ADDRESS).burnFrom(tokenAddress, from, amount); - } -}`} - - -## Best Practices - - -- Initialize the ERC20BurnFacet with the correct storage slot reference during diamond deployment. -- Ensure sufficient token balance and/or allowance before calling `burn` or `burnFrom` respectively. -- Verify that the token contract address passed to the functions is a valid ERC-20 compliant token. - - -## Security Considerations - - -The `burn` and `burnFrom` functions deduct tokens from balances or allowances. Ensure proper access control is implemented at the diamond level to restrict who can call these functions if required. Input validation for token amounts should be handled carefully to prevent unexpected behavior. Reentrancy is not a concern as these functions do not make external calls. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx deleted file mode 100644 index b5405be4..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx +++ /dev/null @@ -1,579 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "Implements the ERC20 token standard for Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements the ERC20 token standard for Compose diamonds. - - - -- Implements all standard ERC20 functions. -- Leverages diamond storage pattern for state management. -- Designed for composability with other diamond facets. - - -## Overview - -The ERC20Facet provides a standard interface for fungible tokens within a Compose diamond. It manages token metadata, balances, allowances, and transfers, enabling composability with other diamond facets. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Facet} from "@compose/diamond/facets/ERC20Facet.sol"; -import {IDiamondCut} from "@compose/diamond/facets/DiamondCutFacet.sol"; - -contract DeployERC20Diamond { - function deploy() external { - // Assume diamondProxy is an instance of a deployed diamond contract - // Assume diamondCutFacetAddress is the address of the DiamondCutFacet - address diamondProxy; // Replace with actual diamond proxy address - address diamondCutFacetAddress; // Replace with actual DiamondCutFacet address - address erc20FacetAddress; // Replace with actual ERC20Facet contract address - - bytes32 erc20FacetCutSelector = keccak256("ERC20Facet"); // Example selector, actual is function signature hash - - IDiamondCut(diamondCutFacetAddress).diamondCut([ - (IDiamondCut.FacetCutOperation.ADD, erc20FacetAddress, erc20FacetCutSelector, "") // Assuming empty init data - ], address(0), ""); - - IERC20Facet erc20 = IERC20Facet(diamondProxy); - - // Example: Mint tokens to deployer - // The ERC20Facet itself might not have a mint function, but a separate facet could handle it. - // For demonstration, assume a hypothetical minting mechanism is in place or handled by another facet. - // erc20.mint(msg.sender, 1000 * 10**18); - - // Example: Check total supply - uint256 totalSupply = erc20.totalSupply(); - - // Example: Approve a spender - erc20.approve(address(1), 100 * 10**18); - - // Example: Check allowance - uint256 allowance = erc20.allowance(msg.sender, address(1)); - } -}`} - - -## Best Practices - - -- Initialize the ERC20Facet with the correct storage slot during diamond deployment. -- Access token data and perform transfers through the diamond proxy address. -- Ensure any token minting or burning logic is handled by a separate, appropriately permissioned facet. - - -## Security Considerations - - -Standard ERC20 security considerations apply, including checks for sufficient balance and allowance. Ensure that access control for token minting/burning is strictly enforced by a separate facet. Reentrancy is mitigated by the standard ERC20 transfer logic and diamond proxy pattern. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx deleted file mode 100644 index 701b0296..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx +++ /dev/null @@ -1,418 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "ERC-20 token functionality for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token functionality for Compose diamonds - - - -- Implements core ERC-20 functions: `transfer`, `transferFrom`, `approve`. -- Supports `mint` and `burn` operations for supply management. -- Exposes internal storage via `getStorage` for direct access by authorized facets. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod module implements standard ERC-20 token operations. It provides essential functions for managing token balances, allowances, and total supply, enabling seamless integration of ERC-20 functionality into your Compose diamond. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod } from "@compose/modules/ERC20/IERC20Mod.sol"; - -contract ERC20ConsumerFacet { - IERC20Mod public immutable erc20Mod; - - constructor(address _erc20ModAddress) { - erc20Mod = IERC20Mod(_erc20ModAddress); - } - - function consumeTransfer(address _from, address _to, uint256 _amount) external { - erc20Mod.transfer(_to, _amount); - } - - function consumeApprove(address _spender, uint256 _amount) external { - erc20Mod.approve(_spender, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC20Mod facet is correctly initialized before use. -- Handle potential errors like `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` robustly. -- When upgrading, preserve the storage layout of the ERC20Mod to maintain state continuity. - - -## Integration Notes - - -The ERC20Mod utilizes a standard storage layout for ERC-20 state variables (balances, allowances, total supply). Facets interacting with this module should be aware of this layout for efficient storage access. The `getStorage` function provides a direct pointer to this struct, allowing for gas-efficient reads and writes when interacting with ERC-20 state. Ensure this facet is added to the diamond's facet registry. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json deleted file mode 100644 index 0b74f444..00000000 --- a/website/docs/library/token/ERC20/ERC20/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 fungible token implementations." - } -} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx deleted file mode 100644 index 2fcbb8aa..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,417 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain transfers for ERC20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Facilitates cross-chain transfers for ERC20 tokens. - - - -- Enables authorized cross-chain minting and burning of ERC20 tokens. -- Leverages diamond access control for `trusted-bridge` role validation. -- Provides internal checks for bridge authenticity. - - -## Overview - -The ERC20BridgeableFacet enables secure and authorized cross-chain token minting and burning operations. It integrates with the diamond's access control to ensure only trusted bridge addresses can perform these sensitive actions, maintaining integrity across different chains. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableFacet} from "../interfaces/IERC20BridgeableFacet.sol"; -import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol"; - -contract Deployer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function mintCrosschain(address _token, address _to, uint256 _amount) external { - bytes4 selector = IERC20BridgeableFacet.crosschainMint.selector; - (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _token, _to, _amount)); - require(success, "Mint failed"); - } - - function burnCrosschain(address _token, address _from, uint256 _amount) external { - bytes4 selector = IERC20BridgeableFacet.crosschainBurn.selector; - (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _token, _from, _amount)); - require(success, "Burn failed"); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is granted only to authorized bridge smart contracts or entities. -- Integrate with the diamond's access control mechanism to manage permissions for cross-chain operations. -- Use the `checkTokenBridge` internal function (or equivalent logic) within other facets to validate bridge caller authenticity before executing sensitive cross-chain logic. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are only callable by addresses holding the `trusted-bridge` role. The `checkTokenBridge` function enforces this role, preventing unauthorized cross-chain operations. Ensure the `trusted-bridge` role is managed carefully to prevent rug pulls or unauthorized token inflation/deflation. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx deleted file mode 100644 index 40440227..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,419 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "Manage cross-chain ERC20 token transfers and burns." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage cross-chain ERC20 token transfers and burns. - - - -- Enables cross-chain minting and burning of ERC20 tokens. -- Enforces `trusted-bridge` role for sensitive cross-chain operations. -- Provides helper functions to access internal storage structs for ERC20 and AccessControl data. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides functionality for cross-chain ERC20 token operations, including minting and burning. It enforces access control to ensure only trusted bridge addresses can perform these sensitive actions, enhancing security for cross-chain interactions within a diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableMod} from "../interfaces/IERC20BridgeableMod.sol"; -import {DiamondStorage} from "../DiamondStorage.sol"; - -contract ERC20BridgeableFacet { - using DiamondStorage for IDiamondStorage; - - function _crosschainMint(address _token, address _to, uint256 _amount) external { - IDiamondStorage.accessControl().checkRole(IDiamondStorage.AccessControlStorage.ACCESS_CONTROL_STORAGE_SLOT, keccak256("trusted-bridge")); - IERC20BridgeableMod(address(this)).crosschainMint(_token, _to, _amount); - } - - function _crosschainBurn(address _token, address _from, uint256 _amount) external { - IDiamondStorage.accessControl().checkRole(IDiamondStorage.AccessControlStorage.ACCESS_CONTROL_STORAGE_SLOT, keccak256("trusted-bridge")); - IERC20BridgeableMod(address(this)).crosschainBurn(_token, _from, _amount); - } -}`} - - -## Best Practices - - -- Ensure that only addresses with the `trusted-bridge` role can call `crosschainMint` and `crosschainBurn` functions by implementing appropriate access control checks. -- Handle potential `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, and `ERC20InvalidReciever` errors to gracefully manage invalid bridge participants or addresses. -- Verify that the `_token` address passed to cross-chain functions is a valid ERC20 contract and that the sender has sufficient balance for `crosschainBurn` operations. - - -## Integration Notes - - -The `ERC20BridgeableMod` relies on the diamond storage pattern. It accesses `ERC20Storage` and `AccessControlStorage` from predefined storage slots within the diamond. The `checkTokenBridge` internal function verifies the caller's role against the `trusted-bridge` role stored in `AccessControlStorage`. Facets integrating with this module should ensure these storage slots are correctly initialized and managed. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json deleted file mode 100644 index 74afd31f..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20 Bridgeable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 Bridgeable extension for ERC-20 tokens." - } -} diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx deleted file mode 100644 index ff89759e..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ /dev/null @@ -1,324 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "Manages ERC-20 token permits for EIP-2612 compliance." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-20 token permits for EIP-2612 compliance. - - - -- Implements EIP-2612 `permit` functionality for ERC-20 tokens. -- Provides `nonces` and `DOMAIN_SEPARATOR` for signature generation and validation. -- Enables gasless allowance approvals by users. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant token permits, allowing users to grant allowances to third parties via signed messages without on-chain transactions. It exposes functions to retrieve nonces, the domain separator, and to process permit signatures, enhancing composability for token interactions. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; -import {ERC20PermitFacet} from "../facets/ERC20PermitFacet.sol"; - -contract MyDiamond is IERC20Permit { - // ... diamond setup ... - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override { - address facetAddress = address(this); // or the actual facet address - bytes4 selector = bytes4(keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")); - - (bool success, ) = facetAddress.call(abi.encodeWithSelector(selector, owner, spender, value, deadline, v, r, s)); - require(success, "ERC20PermitFacet: permit call failed"); - } - - // ... other diamond functions ... -}`} - - -## Best Practices - - -- Store the DOMAIN_SEPARATOR and nonces in persistent storage within the diamond for predictable behavior and gas efficiency. -- Ensure the `deadline` parameter in `permit` is validated to prevent stale or overly long-lived permits. - - -## Security Considerations - - -The `permit` function is critical for managing allowances. Ensure that the signature verification logic correctly uses the `DOMAIN_SEPARATOR` and the owner's nonce to prevent replay attacks and unauthorized approvals. Access to `permit` should be controlled to prevent malicious actors from front-running or manipulating allowance grants. Input validation on `owner`, `spender`, `value`, and `deadline` is essential. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx deleted file mode 100644 index 20934d9c..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ /dev/null @@ -1,276 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "ERC-2612 Permit logic and domain separator" -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2612 Permit logic and domain separator - - - -- Implements ERC-2612 permit logic for gasless token approvals. -- Generates a unique domain separator for signature replay protection. -- Provides functions to access internal permit storage for integration. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the necessary logic and storage for implementing ERC-2612 permit functionality, allowing gasless approvals for ERC-20 tokens. It defines the domain separator and handles the validation and application of permit signatures, enhancing composability and user experience by enabling off-chain authorization. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20PermitMod} from "@compose/modules/ERC20PermitMod.sol"; - -contract MyERC20Facet { - using ERC20PermitMod for ERC20PermitMod.PermitStorage; - - ERC20PermitMod.PermitStorage internal _permitStorage; - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Fetch domain separator from the module - bytes32 domainSeparator = ERC20PermitMod.DOMAIN_SEPARATOR(); - - // Validate and set allowance using the module's permit function - _permitStorage.permit(owner, spender, value, deadline, v, r, s, domainSeparator); - } - - // Other ERC20 functions... -}`} - - -## Best Practices - - -- Ensure the calling facet correctly emits the Approval event after a successful permit operation. -- Implement access control within the calling facet to restrict who can call the `permit` function if necessary. -- Verify that the `deadline` parameter in the permit signature is respected to prevent stale approvals. - - -## Integration Notes - - -The `ERC20PermitMod` module requires its `PermitStorage` struct to be integrated into the diamond's storage layout. Facets that utilize this module must instantiate and manage an instance of `ERC20PermitMod.PermitStorage`. The `permit` function within this module validates the signature and updates the allowance, but the `Approval` event must be emitted by the facet calling the module's function to comply with ERC-20 standards. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json deleted file mode 100644 index 54093a31..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20 Permit", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 Permit extension for ERC-20 tokens." - } -} diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json deleted file mode 100644 index 0b74f444..00000000 --- a/website/docs/library/token/ERC20/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 fungible token implementations." - } -} diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx deleted file mode 100644 index 6660ea67..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ /dev/null @@ -1,529 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "Manages ERC-6909 token balances, allowances, and operator roles." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-6909 token balances, allowances, and operator roles. - - - -- Implements ERC-6909 standard for fungible and non-fungible tokens. -- Supports token transfers, allowance management, and operator roles. -- Provides direct access to underlying storage for efficient data retrieval. - - -## Overview - -The ERC6909Facet implements the ERC-6909 standard for fungible and non-fungible tokens within a Compose diamond. It provides core functionalities for managing token ownership, approvals, and operator relationships, enabling flexible token interactions and composability. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Facet} from "@compose/contracts/facets/ERC6909/IERC6909Facet.sol"; -import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; - -contract DeployERC6909 is IDiamondCut { - address constant ERC6909_FACET_ADDRESS = address(0x...); // Replace with actual deployed address - bytes4 constant TRANSFER_SELECTOR = IERC6909Facet.transfer.selector; - - function deployDiamond() external { - // ... diamond deployment logic ... - - // Add ERC6909Facet - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: ERC6909_FACET_ADDRESS, - action: IDiamondCut.Action.ADD, - selectors: bytes4[](byteset(TRANSFER_SELECTOR)) // Add all selectors for the facet - }); - diamondCut(cuts, address(0), ""); - } - - function interactWithERC6909(address _diamondAddress, address _to, uint256 _amount, bytes32 _id) external { - IERC6909Facet erc6909Facet = IERC6909Facet(_diamondAddress); - erc6909Facet.transfer(_to, _amount, _id); - } -}`} - - -## Best Practices - - -- Initialize the ERC6909Facet with appropriate initial token supply and ownership structures during diamond deployment. -- Access token-specific data (balance, allowance) via the diamond proxy address, leveraging the facet's selectors. -- Utilize `setOperator` judiciously to manage permissions for automated or delegated token transfers. - - -## Security Considerations - - -Ensure that access control for `transferFrom` and `approve` functions is correctly implemented and adheres to expected security patterns. Verify that `setOperator` does not grant unintended broad permissions. Input validation for `_id`, `_amount`, `_to`, and `_from` parameters should be robust to prevent unexpected state changes. - - -
- -
- - diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx deleted file mode 100644 index ab2479fa..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ /dev/null @@ -1,522 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "Manages ERC-6909 minimal multi-token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-6909 minimal multi-token logic. - - - -- Supports minting, burning, and transferring of multiple token IDs. -- Manages token approvals for spenders. -- Allows setting and removing operators for token holders. -- Adheres to ERC-6909 minimal multi-token standard. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the core logic and storage for implementing the ERC-6909 standard. It enables functionalities like minting, burning, transferring, and managing operator approvals for multiple token types within a diamond. By abstracting this logic into a module, diamonds can easily integrate ERC-6909 compliance while adhering to Compose's principles of composability and storage reuse. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Mod} from "@compose/modules/ERC6909Mod.sol"; - -contract MyERC6909Facet { - IERC6909Mod internal immutable _erc6909Mod; - - constructor(address _erc6909ModAddress) { - _erc6909Mod = IERC6909Mod(_erc6909ModAddress); - } - - function mintToken(uint256 _id, uint256 _amount, address _to) external { - _erc6909Mod.mint(_id, _amount, _to); - } - - function approveToken(uint256 _id, address _spender, uint256 _amount) external { - _erc6909Mod.approve(_id, _spender, _amount); - } - - function transferToken(uint256 _id, address _from, address _to, uint256 _amount) external { - _erc6909Mod.transfer(_id, _from, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure the `ERC6909Mod` contract is correctly initialized with its storage slot. -- Handle all custom errors emitted by the module functions to provide clear feedback to users. -- When implementing the `transfer` function, carefully check for operator status to allow seamless transfers by approved agents. - - -## Integration Notes - - -The `ERC6909Mod` utilizes a dedicated storage slot for its state, defined by `STORAGE_POSITION`. Facets interacting with this module should retrieve a pointer to the `ERC6909Storage` struct using the `getStorage` function. This allows for direct manipulation of module state, ensuring that all facets see a consistent view of the ERC-6909 data. The order of fields within the `ERC6909Storage` struct is critical and must be preserved for compatibility with the diamond storage pattern. - - -
- -
- - diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json deleted file mode 100644 index 5653b1ef..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-6909 minimal multi-token implementations." - } -} diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json deleted file mode 100644 index 5653b1ef..00000000 --- a/website/docs/library/token/ERC6909/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-6909 minimal multi-token implementations." - } -} diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx deleted file mode 100644 index 4a6a059a..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ /dev/null @@ -1,218 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "Burn ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC721 tokens within a Compose diamond. - - - -- Implements `burn` function to destroy ERC721 tokens. -- Emits standard `Transfer` event with `from` and `to` set to the zero address upon burning. -- Requires `ERC721NonexistentToken` and `ERC721InsufficientApproval` error checks. - - -## Overview - -The ERC721BurnFacet provides the functionality to destroy ERC721 tokens. It integrates with the diamond proxy pattern by exposing a `burn` function that modifies the token's state and emits standard ERC721 events. This facet is essential for managing the lifecycle of NFTs. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "./interfaces/IERC721BurnFacet.sol"; -import {IDiamondCut} from "./interfaces/IDiamondCut.sol"; - -contract Deployer { - function deploy() public { - // Assume diamond and diamondCut are deployed and initialized - address diamond = address(0x123); - IDiamondCut diamondCut = IDiamondCut(diamond); - - // Facet deployment and cut (simplified) - // address erc721BurnFacetAddress = new ERC721BurnFacet(); - // IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - // cut[0] = IDiamondCut.FacetCut( - // erc721BurnFacetAddress, - // IDiamondCut.FacetCutAction.ADD, - // IDiamondCut.getSelectors(ERC721BurnFacet) - // ); - // diamondCut.diamondCut(cut, address(0), ""); - - IERC721BurnFacet burnFacet = IERC721BurnFacet(diamond); - - // Example: Burn token ID 1 - // Requires appropriate approval or ownership - // burnFacet.burn(1); - } -}`} - - -## Best Practices - - -- Ensure the ERC721BurnFacet is correctly cut into the diamond and its functions are accessible via the diamond proxy. -- Verify ownership or sufficient approval before calling the `burn` function to prevent unauthorized token destruction. - - -## Security Considerations - - -The `burn` function must be protected by appropriate access control mechanisms (ownership or delegated approval). Ensure that the caller has the necessary permissions to burn the specified token. The facet relies on the underlying ERC721 storage layout for token existence and approval checks. Reentrancy is not a concern as the function does not make external calls. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx deleted file mode 100644 index 47ab5dbd..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx +++ /dev/null @@ -1,666 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "Manages ERC721 token ownership, approvals, and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC721 token ownership, approvals, and transfers. - - - -- Full ERC721 compliance for Non-Fungible Tokens. -- Supports both standard and safe token transfers. -- Manages token ownership, individual token approvals, and operator approvals. -- Provides essential metadata functions like name, symbol, and tokenURI. - - -## Overview - -The ERC721Facet provides the core functionality for managing Non-Fungible Tokens (NFTs) within a Compose diamond. It handles token ownership, approvals for individual tokens and operator roles, and facilitates both standard and safe token transfers according to the ERC721 standard. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose-protocol/diamond/contracts/facets/ERC721/IERC721Facet.sol"; - -contract ERC721Consumer { - IERC721Facet public immutable erc721Facet; - - constructor(address _diamondAddress) { - erc721Facet = IERC721Facet(_diamondAddress); - } - - function getTokenName() external view returns (string memory) { - return erc721Facet.name(); - } - - function getTokenSymbol() external view returns (string memory) { - return erc721Facet.symbol(); - } - - function getOwner(uint256 tokenId) external view returns (address) { - return erc721Facet.ownerOf(tokenId); - } - - function transferToken(address to, uint256 tokenId) external { - // Ensure caller is approved or owner - erc721Facet.transferFrom(msg.sender, to, tokenId); - } -}`} - - -## Best Practices - - -- Ensure the diamond proxy is initialized with the ERC721Facet to enable NFT functionality. -- Implement robust access control for functions that modify approvals or transfer tokens, typically requiring the caller to be the owner or an approved address. -- When upgrading or replacing the ERC721Facet, ensure compatibility with existing token data and user expectations. - - -## Security Considerations - - -Access control is paramount. Ensure that only the token owner or an explicitly approved address can initiate transfers or set approvals for a specific token. The `safeTransferFrom` functions include checks for receiver contract compatibility to mitigate reentrancy risks. Input validation on token IDs and addresses is crucial to prevent unexpected behavior or denial of service. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx deleted file mode 100644 index b33a349d..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx +++ /dev/null @@ -1,356 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "Manage ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC721 tokens within a Compose diamond. - - - -- Standard ERC721 token operations: mint, burn, transfer. -- Manages ERC721 state within the diamond's storage using a predefined slot. -- Enforces ERC721 ownership and approval checks during transfers. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod provides the core logic for ERC721 token management. It enables facets to mint, burn, and transfer tokens, adhering to the ERC721 standard. This module abstracts away the complexities of diamond storage for ERC721 state, allowing for robust and composable token functionality. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; -import {IDiamondCut} from "@compose/core/IDiamondCut.sol"; - -contract MyERC721Facet { - // Assume ERC721Mod is deployed and its address is known - address constant ERC721_MOD_ADDRESS = address(0x123); // Replace with actual address - - function safeMint(address _to, uint256 _tokenId) external { - IERC721Mod(ERC721_MOD_ADDRESS).mint(_to, _tokenId); - } - - function safeTransfer(address _from, address _to, uint256 _tokenId) external { - IERC721Mod(ERC721_MOD_ADDRESS).transferFrom(_from, _to, _tokenId); - } - - function safeBurn(uint256 _tokenId) external { - IERC721Mod(ERC721_MOD_ADDRESS).burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721Mod` contract is correctly initialized and accessible via its address. -- Always validate `_to` addresses in `mint` and `transferFrom` calls to prevent sending tokens to zero addresses. -- Handle potential errors like `ERC721IncorrectOwner`, `ERC721NonexistentToken`, and `ERC721InvalidReceiver` appropriately in calling facets. - - -## Integration Notes - - -The ERC721Mod interacts with diamond storage at a fixed slot to manage its internal `ERC721Storage` struct. Facets calling functions within this module will implicitly read from and write to this shared storage. Ensure that no other facets or modules attempt to use the same storage slot to avoid state conflicts. The `getStorage` function can be used by facets to inspect the current ERC721 state. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json deleted file mode 100644 index 5fdbd55a..00000000 --- a/website/docs/library/token/ERC721/ERC721/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-721 non-fungible token implementations." - } -} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index 76a63c86..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,226 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "Burn ERC721 tokens and manage enumeration state." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC721 tokens and manage enumeration state. - - - -- Allows burning of ERC721 tokens. -- Integrates with enumeration logic to remove burned tokens from tracking. -- Emits a `Transfer` event with `from` and `to` addresses set to the zero address for burned tokens. - - -## Overview - -The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens within a Compose diamond. It ensures that burned tokens are correctly removed from enumeration tracking, maintaining the integrity of the token supply and ownership data. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableBurnFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; - -contract BurnExample { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 _tokenId) external { - IERC721EnumerableBurnFacet(diamondAddress).burn(_tokenId); - } - - // Example of getting storage (for inspection or advanced logic) - function getFacetStorage() external view returns (bytes memory) { - return IERC721EnumerableBurnFacet(diamondAddress).getStorage(); - } -}`} - - -## Best Practices - - -- Ensure proper access control is implemented at the diamond level before calling the `burn` function to prevent unauthorized token destruction. -- Verify that the token ID provided to `burn` actually exists to avoid `ERC721NonexistentToken` errors. -- Understand that burning a token is irreversible and removes it from all enumeration. - - -## Security Considerations - - -The `burn` function requires that the caller either be the owner of the token or have been granted sufficient approval. Failure to meet these conditions will result in an `ERC721InsufficientApproval` error. Additionally, calling `burn` on a non-existent token will revert with `ERC721NonexistentToken`. Ensure that the diamond's access control layer correctly restricts who can call these functions. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx deleted file mode 100644 index fd78c62f..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,748 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "Enumerable ERC-721 implementation for token tracking." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enumerable ERC-721 implementation for token tracking. - - - -- Full ERC-721 compliance with enumerable extensions. -- Efficient on-chain tracking of token supply and ownership. -- Provides indexed access to tokens owned by an address. - - -## Overview - -This facet provides a complete ERC-721 implementation with enumerable extensions, enabling efficient tracking of token supply, ownership counts, and individual token ownership by index. It ensures standard ERC-721 functionality while adding robust on-chain querying capabilities. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; - -contract MyDiamond { - // ... diamond setup - - function name() external view returns (string memory) { - return ERC721EnumerableFacet.name(); - } - - function symbol() external view returns (string memory) { - return ERC721EnumerableFacet.symbol(); - } - - function tokenURI(uint256 tokenId) external view returns (string memory) { - return ERC721EnumerableFacet.tokenURI(tokenId); - } - - function totalSupply() external view returns (uint256) { - return ERC721EnumerableFacet.totalSupply(); - } - - function balanceOf(address owner) external view returns (uint256) { - return ERC721EnumerableFacet.balanceOf(owner); - } - - function ownerOf(uint256 tokenId) external view returns (address) { - return ERC721EnumerableFacet.ownerOf(tokenId); - } - - function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256) { - return ERC721EnumerableFacet.tokenOfOwnerByIndex(owner, index); - } - - // ... other functions -}`} - - -## Best Practices - - -- Ensure proper initialization of the ERC721EnumerableFacet storage within the diamond's initialization logic. -- Access token-specific information using `ownerOf`, `tokenURI`, and `tokenOfOwnerByIndex` for clarity and gas efficiency. -- Leverage `balanceOf` and `totalSupply` for quick on-chain state checks. - - -## Security Considerations - - -Input validation is crucial for all token ID and address parameters to prevent errors and unexpected behavior. Ensure that approvals are managed correctly to maintain ownership integrity. Reentrancy is not a direct concern for read-only enumerable functions, but state-changing functions like `transferFrom` and `safeTransferFrom` must follow standard ERC-721 reentrancy guards. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx deleted file mode 100644 index 1a815a84..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,348 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "Manages ERC-721 tokens with enumeration support." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-721 tokens with enumeration support. - - - -- Supports minting new ERC-721 tokens and adding them to enumeration lists. -- Enables burning of existing ERC-721 tokens, removing them from enumeration. -- Manages token transfers while updating internal ownership and enumeration data. -- Provides a `getStorage` function to access the internal enumerable storage struct. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the core logic for implementing enumerable ERC-721 functionality within a Compose diamond. It handles token minting, burning, and transfers while maintaining accurate lists of all owned tokens for each address and the total supply. This ensures compliance with ERC-721 standards and allows for efficient querying of token ownership. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod} from "@compose/modules/erc721/ERC721EnumerableMod.sol"; - -contract MyERC721Facet { - IERC721EnumerableMod internal immutable erc721Mod; - - constructor(address _diamondProxy) { - erc721Mod = IERC721EnumerableMod(_diamondProxy); - } - - function safeMint(address to, uint256 tokenId) external { - // Add any custom checks here before minting - erc721Mod.mint(to, tokenId); - // Add any custom logic after minting here - } - - function safeTransferFrom(address from, address to, uint256 tokenId) external { - // Add any custom checks here before transferring - erc721Mod.transferFrom(from, to, tokenId); - // Add any custom logic after transferring here - } - - function destroyToken(uint256 tokenId) external { - // Add any custom checks here before burning - erc721Mod.burn(tokenId); - // Add any custom logic after burning here - } -}`} - - -## Best Practices - - -- Ensure the `ERC721EnumerableMod` facet is correctly initialized and accessible via the diamond proxy. -- Implement necessary access control and validation logic within your custom facets before calling module functions like `mint`, `burn`, or `transferFrom`. -- Handle potential reverts from module functions, such as `ERC721IncorrectOwner` or `ERC721NonexistentToken`, appropriately in your facet logic. - - -## Integration Notes - - -The `ERC721EnumerableMod` module interacts with a predefined storage slot within the diamond's storage layout to manage the state of enumerable ERC-721 tokens. This includes mapping token IDs to owners, tracking token ownership for enumeration, and maintaining the total supply. Facets integrating with this module should be aware that state changes made through the module's functions (mint, burn, transferFrom) directly affect the diamond's storage and are immediately visible to all other facets. The order of storage variables within the `ERC721EnumerableStorage` struct is critical and must not be altered to maintain compatibility. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json deleted file mode 100644 index 6ab22b34..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-721 Enumerable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-721 Enumerable extension for ERC-721 tokens." - } -} diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json deleted file mode 100644 index 5fdbd55a..00000000 --- a/website/docs/library/token/ERC721/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-721 non-fungible token implementations." - } -} diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx deleted file mode 100644 index 9a2a8bfc..00000000 --- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "Handles royalty information for tokens and sales." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Handles royalty information for tokens and sales. - - - -- Implements ERC-2981 `royaltyInfo` standard. -- Supports token-specific royalty configurations. -- Falls back to a default royalty percentage. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard, providing a standardized way to query royalty information for NFTs. It allows for setting token-specific royalties and falls back to a default royalty, ensuring creators are compensated on secondary sales. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyFacet} from "@compose-protocol/diamond/contracts/facets/Royalty/IRoyaltyFacet.sol"; - -contract RoyaltyConsumer { - address public diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { - bytes4 selector = IRoyaltyFacet.royaltyInfo.selector; - (receiver, royaltyAmount) = IRoyaltyFacet(diamondAddress).royaltyInfo(selector, _tokenId, _salePrice); - return (receiver, royaltyAmount); - } -}`} - - -## Best Practices - - -- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. -- Ensure the `royaltyInfo` function is callable by external marketplaces or consumers. -- Store royalty configurations off-chain or in a separate, permissioned facet if dynamic updates are required. - - -## Security Considerations - - -Access to modify royalty settings (if implemented in a separate facet) must be strictly controlled. Ensure the calculation of royalty amounts prevents integer overflow or underflow. The `royaltyInfo` function itself is read-only and poses no direct reentrancy risk. - - -
- -
- - diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx deleted file mode 100644 index 4286283e..00000000 --- a/website/docs/library/token/Royalty/RoyaltyMod.mdx +++ /dev/null @@ -1,365 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "Manage ERC-2981 royalties for tokens and defaults." -gitSource: "https://github.com/maxnorm/Compose/blob/ff6fa8311504bcc485d7920faddca96c4144627c/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC-2981 royalties for tokens and defaults. - - - -- Implements ERC-2981 standard for on-chain royalty attribution. -- Supports both token-specific and default royalty configurations. -- Provides functions to query royalty information based on token ID and sale price. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides a standardized implementation for ERC-2981 royalty payments. It allows setting both default royalties applicable to all tokens and specific royalties for individual tokens, ensuring compliance with the royalty standard. This enhances composability by providing a predictable mechanism for creators to receive royalties on secondary sales. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "../interfaces/IRoyaltyMod.sol"; -import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol"; - -contract MyFacet is IERC2981 { - address immutable DIAMOND_ADDRESS; - IRoyaltyMod private royaltyMod; - - constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; - royaltyMod = IRoyaltyMod(diamondAddress); - } - - /** - * @notice Gets royalty information for a token and sale price. - * @param tokenId The ID of the token. - * @param salePrice The sale price of the token. - * @return receiver The address receiving the royalty. - * @return royaltyAmount The amount of royalty. - */ - function royaltyInfo(uint256 tokenId, uint256 salePrice) external view override returns (address receiver, uint256 royaltyAmount) { - (receiver, royaltyAmount) = royaltyMod.royaltyInfo(tokenId, salePrice); - } - - /** - * @notice Sets royalty information for a specific token. - * @param tokenId The ID of the token. - * @param receiver The address receiving the royalty. - * @param feeBasisPoints The royalty fee in basis points (e.g., 100 for 1%). - */ - function setTokenRoyalty(uint256 tokenId, address receiver, uint16 feeBasisPoints) external { - royaltyMod.setTokenRoyalty(tokenId, receiver, feeBasisPoints); - } - - /** - * @notice Sets default royalty information for all tokens. - * @param receiver The address receiving the default royalty. - * @param feeBasisPoints The default royalty fee in basis points. - */ - function setDefaultRoyalty(address receiver, uint16 feeBasisPoints) external { - royaltyMod.setDefaultRoyalty(receiver, feeBasisPoints); - } -} -`} - - -## Best Practices - - -- Use `setTokenRoyalty` to configure specific token royalties and `setDefaultRoyalty` for general fallback, ensuring a clear hierarchy. -- Validate receiver addresses and fee basis points rigorously before setting royalties to prevent errors and unexpected payouts. -- Handle `ERC2981InvalidDefaultRoyalty`, `ERC2981InvalidDefaultRoyaltyReceiver`, `ERC2981InvalidTokenRoyalty`, and `ERC2981InvalidTokenRoyaltyReceiver` errors to gracefully manage invalid royalty configurations. - - -## Integration Notes - - -The RoyaltyMod utilizes a dedicated storage slot for its state, containing `DefaultRoyalty` and `TokenRoyalty` mappings. The `getStorage` function provides direct access to this struct. Facets interacting with royalty logic should call the module's functions to ensure consistent and correct handling of royalty data. Changes to default or token-specific royalties are immediately reflected in subsequent `royaltyInfo` calls. - - -
- -
- - diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json deleted file mode 100644 index 95a86a02..00000000 --- a/website/docs/library/token/Royalty/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Royalty", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-2981 royalty standard implementations." - } -} diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json deleted file mode 100644 index a4461cfd..00000000 --- a/website/docs/library/token/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Token Standards", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Token standard implementations for Compose diamonds." - } -} From 75e2e68f9cd44e6a24767abe937e3f91886c823f Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 15:52:30 -0500 Subject: [PATCH 45/68] fix sync generator --- .github/scripts/generate-docs-utils/category-generator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/generate-docs-utils/category-generator.js b/.github/scripts/generate-docs-utils/category-generator.js index ed4f769e..229e015e 100644 --- a/.github/scripts/generate-docs-utils/category-generator.js +++ b/.github/scripts/generate-docs-utils/category-generator.js @@ -487,7 +487,7 @@ function syncDocsStructure() { const pathParts = relativePath.split('/'); const mappedPathParts = pathParts.map(part => mapDirectoryName(part)); const mappedRelativePath = mappedPathParts.join('/'); - const outputDir = path.join(libraryDir, mappedPathParts); + const outputDir = path.join(libraryDir, ...mappedPathParts); const wasCreated = createCategoryFile( outputDir, From e6108aecf869c9da3ed05810119d19c5171a360a Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sun, 21 Dec 2025 20:57:30 +0000 Subject: [PATCH 46/68] docs: auto-generate docs pages from NatSpec --- website/docs/library/_category_.json | 12 + .../AccessControl/AccessControlFacet.mdx | 554 +++++++++++++ .../access/AccessControl/AccessControlMod.mdx | 443 +++++++++++ .../access/AccessControl/_category_.json | 11 + .../AccessControlPausableFacet.mdx | 397 ++++++++++ .../AccessControlPausableMod.mdx | 379 +++++++++ .../AccessControlPausable/_category_.json | 11 + .../AccessControlTemporalFacet.mdx | 461 +++++++++++ .../AccessControlTemporalMod.mdx | 477 +++++++++++ .../AccessControlTemporal/_category_.json | 11 + .../docs/library/access/Owner/OwnerFacet.mdx | 211 +++++ .../docs/library/access/Owner/OwnerMod.mdx | 258 ++++++ .../docs/library/access/Owner/_category_.json | 11 + .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 291 +++++++ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 309 ++++++++ .../access/OwnerTwoSteps/_category_.json | 11 + website/docs/library/access/_category_.json | 11 + .../docs/library/diamond/DiamondCutFacet.mdx | 422 ++++++++++ .../docs/library/diamond/DiamondCutMod.mdx | 396 ++++++++++ .../library/diamond/DiamondLoupeFacet.mdx | 254 ++++++ website/docs/library/diamond/DiamondMod.mdx | 237 ++++++ website/docs/library/diamond/_category_.json | 11 + .../diamond/example/ExampleDiamond.mdx | 129 +++ .../library/diamond/example/_category_.json | 11 + .../interfaceDetection/ERC165/ERC165Mod.mdx | 157 ++++ .../interfaceDetection/ERC165/_category_.json | 11 + .../interfaceDetection/_category_.json | 11 + .../library/token/ERC1155/ERC1155Facet.mdx | 678 ++++++++++++++++ .../docs/library/token/ERC1155/ERC1155Mod.mdx | 605 ++++++++++++++ .../library/token/ERC1155/_category_.json | 11 + .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 256 ++++++ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 564 +++++++++++++ .../library/token/ERC20/ERC20/ERC20Mod.mdx | 425 ++++++++++ .../library/token/ERC20/ERC20/_category_.json | 11 + .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 417 ++++++++++ .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 431 ++++++++++ .../ERC20/ERC20Bridgeable/_category_.json | 11 + .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 340 ++++++++ .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 284 +++++++ .../token/ERC20/ERC20Permit/_category_.json | 11 + .../docs/library/token/ERC20/_category_.json | 11 + .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 525 +++++++++++++ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 518 ++++++++++++ .../token/ERC6909/ERC6909/_category_.json | 11 + .../library/token/ERC6909/_category_.json | 11 + .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 212 +++++ .../token/ERC721/ERC721/ERC721Facet.mdx | 664 ++++++++++++++++ .../library/token/ERC721/ERC721/ERC721Mod.mdx | 358 +++++++++ .../token/ERC721/ERC721/_category_.json | 11 + .../ERC721EnumerableBurnFacet.mdx | 221 ++++++ .../ERC721EnumerableFacet.mdx | 739 ++++++++++++++++++ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 344 ++++++++ .../ERC721/ERC721Enumerable/_category_.json | 11 + .../docs/library/token/ERC721/_category_.json | 11 + .../library/token/Royalty/RoyaltyFacet.mdx | 188 +++++ .../docs/library/token/Royalty/RoyaltyMod.mdx | 382 +++++++++ .../library/token/Royalty/_category_.json | 11 + website/docs/library/token/_category_.json | 11 + .../docs/library/utils/NonReentrancyMod.mdx | 137 ++++ website/docs/library/utils/_category_.json | 11 + 60 files changed, 13928 insertions(+) create mode 100644 website/docs/library/_category_.json create mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx create mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx create mode 100644 website/docs/library/access/AccessControl/_category_.json create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx create mode 100644 website/docs/library/access/AccessControlPausable/_category_.json create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json create mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx create mode 100644 website/docs/library/access/Owner/OwnerMod.mdx create mode 100644 website/docs/library/access/Owner/_category_.json create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json create mode 100644 website/docs/library/access/_category_.json create mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx create mode 100644 website/docs/library/diamond/DiamondCutMod.mdx create mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx create mode 100644 website/docs/library/diamond/DiamondMod.mdx create mode 100644 website/docs/library/diamond/_category_.json create mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx create mode 100644 website/docs/library/diamond/example/_category_.json create mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json create mode 100644 website/docs/library/interfaceDetection/_category_.json create mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx create mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx create mode 100644 website/docs/library/token/ERC1155/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json create mode 100644 website/docs/library/token/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json create mode 100644 website/docs/library/token/ERC721/_category_.json create mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx create mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx create mode 100644 website/docs/library/token/Royalty/_category_.json create mode 100644 website/docs/library/token/_category_.json create mode 100644 website/docs/library/utils/NonReentrancyMod.mdx create mode 100644 website/docs/library/utils/_category_.json diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json new file mode 100644 index 00000000..720acf5e --- /dev/null +++ b/website/docs/library/_category_.json @@ -0,0 +1,12 @@ +{ + "label": "Library", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library", + "title": "Library Reference", + "description": "API reference for all Compose modules and facets." + } +} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx new file mode 100644 index 00000000..6ac1d29e --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -0,0 +1,554 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Manages roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages roles and permissions within a diamond. + + + +- Hierarchical role management: roles can have admin roles. +- Batch operations for granting and revoking roles. +- Explicit revert reasons for unauthorized access. + + +## Overview + +The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows defining roles, assigning them to addresses, and enforcing permissions on function calls. This facet is crucial for managing administrative privileges and controlling access to sensitive operations. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondProxy} from "@compose-protocol/diamond-proxy/DiamondProxy.sol"; +import {AccessControlFacet} from "@compose-protocol/diamond-proxy/facets/AccessControl/AccessControlFacet.sol"; + +diamond contract MyDiamond is DiamondProxy { + // Facet selectors + bytes4 private constant ACCESS_CONTROL_GRANT_ROLE_SELECTOR = AccessControlFacet.grantRole.selector; + bytes4 private constant ACCESS_CONTROL_HAS_ROLE_SELECTOR = AccessControlFacet.hasRole.selector; + + // Define roles + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + + function upgrade() external { + // ... deployment logic ... + } + + // Example of a function protected by a role + function mintTokens(address _to, uint256 _amount) external { + // Call the AccessControlFacet to check role + (bool success, ) = address(this).call(abi.encodeWithSelector(ACCESS_CONTROL_HAS_ROLE_SELECTOR, MINTER_ROLE, msg.sender)); + require(success, "Role check failed"); + + // ... minting logic ... + } + + // Example of granting a role + function grantAdminRole(address _account) external { + // Call the AccessControlFacet to grant role + (bool success, ) = address(this).call(abi.encodeWithSelector(ACCESS_CONTROL_GRANT_ROLE_SELECTOR, ADMIN_ROLE, _account)); + require(success, "Grant role failed"); + } +}`} + + +## Best Practices + + +- Initialize roles and assign initial admin privileges during diamond deployment. +- Use `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple role assignments. +- Define custom roles using `keccak256` for granular permission control. + + +## Security Considerations + + +Ensure that the initial deployment correctly assigns administrative roles to trusted accounts. The `setRoleAdmin` function must be carefully protected to prevent unauthorized changes to role hierarchies. All functions that modify roles or grant permissions should be callable only by the designated role administrators. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx new file mode 100644 index 00000000..d5276a05 --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -0,0 +1,443 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Manage roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within a diamond. + + + +- Role-based access control for granular permission management. +- Functions to grant, revoke, and check for role ownership (`grantRole`, `revokeRole`, `hasRole`). +- Ability to define and manage administrative roles for other roles (`setRoleAdmin`). + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControl module provides a robust framework for managing role-based access control within Compose diamonds. It enables fine-grained permission management, ensuring that only authorized accounts can execute specific functions. This is critical for maintaining the integrity and security of complex diamond applications. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose/diamond-core/contracts/modules/accesscontrol/IAccessControl.sol"; + +contract MyFacet { + IAccessControl accessControl; + + constructor(address _diamondProxy) { + accessControl = IAccessControl(_diamondProxy); + } + + function grantAdminRole(address _account) external { + bytes32 adminRole = accessControl.getStorage().DEFAULT_ADMIN_ROLE; + accessControl.grantRole(adminRole, _account); + } + + function isOwner(address _account) external view returns (bool) { + bytes32 adminRole = accessControl.getStorage().DEFAULT_ADMIN_ROLE; + return accessControl.hasRole(adminRole, _account); + } +}`} + + +## Best Practices + + +- Use `requireRole` for enforcing access control checks directly within facet functions, reverting with `AccessControlUnauthorizedAccount` if unauthorized. +- Ensure roles and their admin roles are clearly defined and managed, ideally through dedicated administrative facets or initialization scripts. +- Treat role grants and revokes as sensitive operations, implementing appropriate access controls for managing these functions themselves. + + +## Integration Notes + + +This module relies on a dedicated storage slot within the diamond's storage. Facets interact with it via the `IAccessControl` interface. Changes to role assignments or admin roles are immediately reflected and visible to all facets through the diamond proxy. The `getStorage()` function provides direct access to the module's internal state, enabling facets to query role assignments and configurations. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json new file mode 100644 index 00000000..312754c7 --- /dev/null +++ b/website/docs/library/access/AccessControl/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Access Control", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/access/AccessControl", + "description": "Role-based access control (RBAC) pattern." + } +} diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..2a11981d --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -0,0 +1,397 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Manage roles and pausing functionality for diamond access control." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and pausing functionality for diamond access control. + + + +- Role-specific pausing: Allows individual roles to be paused independently. +- Admin-controlled pausing: Only the designated admin of a role can pause or unpause it. +- Integrated access control checks: `requireRoleNotPaused` enforces role activity. + + +## Overview + +This facet provides granular control over role-based access and allows for temporary pausing of specific roles. It integrates with the diamond's storage to manage role states and enforce pausing conditions, ensuring that only authorized actions can be performed when a role is active. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IComposeDiamond} from "@compose-protocol/diamond-contracts/contracts/interfaces/IComposeDiamond.sol"; +import {AccessControlPausableFacet} from "@compose-protocol/diamond-contracts/contracts/facets/AccessControlPausableFacet.sol"; + +contract Deployer { + address diamondAddress; + + function deploy() external { + // Assume diamondAddress is already set or deployed + diamondAddress = address(1); // Placeholder + + // Add AccessControlPausableFacet to the diamond + // Function selectors for AccessControlPausableFacet + bytes4[] memory selectors = new bytes4[](7); + selectors[0] = AccessControlPausableFacet.getAccessControlStorage.selector; + selectors[1] = AccessControlPausableFacet.getStorage.selector; + selectors[2] = AccessControlPausableFacet.isRolePaused.selector; + selectors[3] = AccessControlPausableFacet.pauseRole.selector; + selectors[4] = AccessControlPausableFacet.unpauseRole.selector; + selectors[5] = AccessControlPausableFacet.requireRoleNotPaused.selector; + // Add other selectors if needed, e.g., grantRole, revokeRole from AccessControl facet + + IComposeDiamond(diamondAddress).diamondCut( + new IComposeDiamond.FacetCut[]{ + (AccessControlPausableFacet.attach(address(0)), IComposeDiamond.FacetCutAction.Add, selectors) + }, + address(0), // target init contract + bytes("") // init data + ); + } + + function pauseMyRole() external { + address accessControlPausableFacetAddress = IComposeDiamond(diamondAddress).getFacetAddress(AccessControlPausableFacet.getStorage.selector); + bytes32 role = keccak256("MY_ROLE"); // Example role + AccessControlPausableFacet(accessControlPausableFacetAddress).pauseRole(role); + } + + function unpauseMyRole() external { + address accessControlPausableFacetAddress = IComposeDiamond(diamondAddress).getFacetAddress(AccessControlPausableFacet.getStorage.selector); + bytes32 role = keccak256("MY_ROLE"); // Example role + AccessControlPausableFacet(accessControlPausableFacetAddress).unpauseRole(role); + } + + function checkRoleStatus() external view returns (bool) { + address accessControlPausableFacetAddress = IComposeDiamond(diamondAddress).getFacetAddress(AccessControlPausableFacet.getStorage.selector); + bytes32 role = keccak256("MY_ROLE"); // Example role + return AccessControlPausableFacet(accessControlPausableFacetAddress).isRolePaused(role); + } +}`} + + +## Best Practices + + +- Ensure the `AccessControlFacet` is also deployed and configured for role management before using this facet for pausing. +- The `pauseRole` and `unpauseRole` functions require the caller to be the admin of the role, so proper role administration is crucial. +- Use `requireRoleNotPaused` within other facets to enforce role availability before executing sensitive operations. + + +## Security Considerations + + +Access control relies on the underlying AccessControl system correctly assigning role admins. Incorrect admin assignment could lead to unauthorized pausing or unpausing. The `requireRoleNotPaused` function prevents execution if a role is paused, mitigating risks associated with operations that should not occur during a pause. Ensure that the caller has the necessary permissions to call `pauseRole` and `unpauseRole` to prevent unauthorized state changes. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx new file mode 100644 index 00000000..e0c25f8b --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -0,0 +1,379 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Manages role-based access control with pausing capabilities." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages role-based access control with pausing capabilities. + + + +- Role-specific pausing: Allows individual roles to be paused independently of others. +- Integrated access control checks: Combines role membership verification with pause status. +- Reverts with specific errors: Provides `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` for clear error handling. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module integrates role-based access control with pausing functionality, allowing specific roles to be temporarily suspended. It ensures that operations protected by a role are only executable when that role is not paused, enhancing safety and control during upgrades or emergencies. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableMod} from "@compose/modules/access-control-pausable/IAccessControlPausableMod.sol"; + +contract MyFacet { + IAccessControlPausableMod public immutable accessControlPausableMod; + + constructor(address _accessControlPausableMod) { + accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); + } + + uint256 public constant MY_ROLE = 1; + + function doSomethingProtected() external { + accessControlPausableMod.requireRoleNotPaused(MY_ROLE); + // ... protected logic here ... + } + + function pauseMyRole() external { + // Only an authorized entity can pause + accessControlPausableMod.pauseRole(MY_ROLE); + } + + function unpauseMyRole() external { + // Only an authorized entity can unpause + accessControlPausableMod.unpauseRole(MY_ROLE); + } +}`} + + +## Best Practices + + +- Implement `requireRoleNotPaused` at the entry point of functions requiring role protection to enforce access control and pause status. +- Use `pauseRole` and `unpauseRole` judiciously, typically managed by a separate administrative role or owner, to control operational availability. +- Ensure the `MY_ROLE` identifier used in the facet is consistent with the role identifier managed by the AccessControl module. + + +## Integration Notes + + +This module interacts with the diamond's storage, typically requiring the `AccessControlPausableMod` struct to be stored in a dedicated slot. Facets can access the module's functionality via its interface. The `requireRoleNotPaused` function checks both role membership (delegated to the AccessControl component) and the pause status managed by this module. Ensure that the AccessControl module is initialized and roles are defined before using this module's pausing features. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json new file mode 100644 index 00000000..351b5058 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Pausable Access Control", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/access/AccessControlPausable", + "description": "RBAC with pause functionality." + } +} diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..618bf8b0 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -0,0 +1,461 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Manages time-bound role assignments within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages time-bound role assignments within a diamond. + + + +- Time-bound role granting with explicit expiration timestamps. +- Automatic expiration enforcement via `isRoleExpired` and `requireValidRole`. +- Admin-only control for granting and revoking temporal roles. + + +## Overview + +This facet extends Compose's access control system by introducing time-bound roles. It allows administrators to grant roles with specific expiration timestamps and enforce these expirations, providing granular control over permissions over time. This is crucial for temporary access needs or managing roles that should automatically deactivate. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-protocol/diamond-contracts/diamond/IDiamondCut.sol"; +import {AccessControlTemporalFacet} from "./facets/AccessControlTemporalFacet.sol"; + +contract DeployDiamond { + // ... other facet addresses ... + address accessControlTemporalFacetAddress; + + function deploy() public { + // ... deployment logic ... + + address[] memory facetAddresses = new address[](1); + facetAddresses[0] = accessControlTemporalFacetAddress; + + bytes32[] memory functionSelectors = new bytes32[](7); + // Placeholder for actual selectors + functionSelectors[0] = AccessControlTemporalFacet.getAccessControlStorage.selector; + functionSelectors[1] = AccessControlTemporalFacet.getStorage.selector; + functionSelectors[2] = AccessControlTemporalFacet.getRoleExpiry.selector; + functionSelectors[3] = AccessControlTemporalFacet.isRoleExpired.selector; + functionSelectors[4] = AccessControlTemporalFacet.grantRoleWithExpiry.selector; + functionSelectors[5] = AccessControlTemporalFacet.revokeTemporalRole.selector; + functionSelectors[6] = AccessControlTemporalFacet.requireValidRole.selector; + + // ... diamond cut data ... + + // Example role grant + uint64 expiryTimestamp = uint64(block.timestamp) + 3600; // Role expires in 1 hour + AccessControlTemporalFacet(accessControlTemporalFacetAddress).grantRoleWithExpiry(bytes32("ROLE_TEMPORARY"), msg.sender, expiryTimestamp); + + // Example role check + if (AccessControlTemporalFacet(accessControlTemporalFacetAddress).isRoleExpired(bytes32("ROLE_TEMPORARY"), msg.sender)) { + // Role has expired + } + } +}`} + + +## Best Practices + + +- Grant roles with expiry only when necessary for temporary access, ensuring the `admin` of the role is the caller. +- Regularly audit temporal role assignments to prevent unintended persistent access after intended expiry. +- Use `requireValidRole` within other facets to enforce time-bound access control checks before critical operations. + + +## Security Considerations + + +Access to grant and revoke temporal roles is restricted to the role's administrator, preventing unauthorized parties from manipulating time-bound permissions. The `requireValidRole` function prevents reentrancy by reverting if the role has expired. Ensure that the `admin` role itself is properly secured to prevent unauthorized temporal role management. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..610fc473 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -0,0 +1,477 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Manages time-bound role assignments for access control." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages time-bound role assignments for access control. + + + +- Grants roles with a specified expiry timestamp, automating revocation. +- Provides a `requireValidRole` function to enforce non-expired role checks. +- Offers functions to query role expiry status and revoke temporal roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module introduces temporal access control, allowing roles to be granted with specific expiry timestamps. It ensures that access is automatically revoked once the validity period ends, enhancing security and manageability for diamond applications. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; + +contract MyDiamondFacet { + IAccessControlTemporalMod public immutable accessControlTemporalMod; + + constructor(address _accessControlTemporalMod) { + accessControlTemporalMod = IAccessControlTemporalMod(_accessControlTemporalMod); + } + + function grantAdminRoleWithExpiry(address _user, uint64 _expiry) external { + // Assuming 'DEFAULT_ADMIN_ROLE' is a constant defined elsewhere + bytes32 role = DEFAULT_ADMIN_ROLE; + accessControlTemporalMod.grantRoleWithExpiry(role, _user, _expiry); + } + + function checkAdminAccess(address _user) external view { + bytes32 role = DEFAULT_ADMIN_ROLE; + accessControlTemporalMod.requireValidRole(role, _user); + // Access is permitted + } +}`} + + +## Best Practices + + +- Utilize `grantRoleWithExpiry` to assign roles with clear expiration dates, reducing the need for manual revocation. +- Implement checks using `requireValidRole` before critical operations to ensure active and unexpired role assignments. +- Ensure the `AccessControlTemporalMod` facet is deployed and its address is correctly referenced by facets requiring temporal access control. + + +## Integration Notes + + +This module manages its state within its own storage slots, separate from the core Access Control storage. Facets interacting with this module should call its functions directly. The `requireValidRole` function will revert with `AccessControlRoleExpired` if the role's timestamp has passed, and `AccessControlUnauthorizedAccount` if the role is not assigned at all. Ensure the module is initialized and accessible via its facet address. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json new file mode 100644 index 00000000..3d0a61d6 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Temporal Access Control", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/access/AccessControlTemporal", + "description": "Time-limited role-based access control." + } +} diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx new file mode 100644 index 00000000..39dd27a4 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -0,0 +1,211 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Manages contract ownership and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership and transfers. + + + +- Manages contract ownership. +- Supports transferring ownership to a new address. +- Allows for renouncing ownership. + + +## Overview + +The OwnerFacet provides essential ownership management capabilities for Compose diamonds. It allows the current owner to transfer ownership to a new address or renounce ownership entirely, ensuring clear control and accountability for administrative actions within the diamond. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/facets/owner/IOwnerFacet.sol"; + +contract ExampleOwnerUsage { + IOwnerFacet private immutable _ownerFacet; + + constructor(address ownerFacetAddress) { + _ownerFacet = IOwnerFacet(ownerFacetAddress); + } + + function getCurrentOwner() external view returns (address) { + return _ownerFacet.owner(); + } + + function transferControl(address _newOwner) external { + _ownerFacet.transferOwnership(_newOwner); + } + + function giveUpOwnership() external { + _ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize the diamond with the owner address using the `transferOwnership` function during deployment. +- Ensure only the current owner can call `transferOwnership` and `renounceOwnership`. +- Handle ownership transfer carefully; consider using a multisig for critical contracts. + + +## Security Considerations + + +Access control is critical. Only the current owner should be able to execute ownership-related functions. Setting the new owner to `address(0)` effectively renounces ownership, making the contract effectively immutable regarding ownership changes unless re-initialized by a separate mechanism. Ensure the caller of `transferOwnership` is indeed the current owner. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx new file mode 100644 index 00000000..ae23e7d3 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -0,0 +1,258 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "Manages contract ownership according to ERC-173." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership according to ERC-173. + + + +- Implements ERC-173 contract ownership. +- Provides `owner()`, `transferOwnership()`, and `requireOwner()` functions. +- Supports ownership renouncement by transferring to `address(0)`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The OwnerMod provides a standardized way to manage contract ownership, adhering to the ERC-173 standard. It enables secure ownership transfers and provides a mechanism for owner-only access control, crucial for administrative functions within a diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; + +contract MyFacet { + uint256 constant OWNER_STORAGE_SLOT = 1; // Example slot + + struct OwnerStorage { + address owner; + // other storage variables... + } + + function _getOwnerStorage() internal pure returns (OwnerStorage storage) { + assembly { + storage.slot := OWNER_STORAGE_SLOT + } + } + + function owner() public view returns (address) { + return _getOwnerStorage().owner; + } + + function transferOwnership(address _newOwner) external { + IOwnerMod(_getOwnerStorage()).transferOwnership(_newOwner); + } + + function requireOwner() external view { + IOwnerMod(_getOwnerStorage()).requireOwner(); + } +}`} + + +## Best Practices + + +- Use `transferOwnership` to change the contract owner. Setting the new owner to `address(0)` renounces ownership. +- Employ `requireOwner` to restrict access to sensitive administrative functions to the current owner. +- Ensure the `OwnerMod` storage slot is correctly defined and not duplicated by other facets. + + +## Integration Notes + + +The OwnerMod utilizes a dedicated storage slot to store the owner's address. Facets can access this storage via the `getStorage` function or by directly referencing the defined storage slot. Any facet can read the owner address, but only the current owner can execute administrative functions protected by `requireOwner` or initiate ownership transfers. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json new file mode 100644 index 00000000..f24d6058 --- /dev/null +++ b/website/docs/library/access/Owner/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Owner", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/access/Owner", + "description": "Single-owner access control pattern." + } +} diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..da08a336 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -0,0 +1,291 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Manages contract ownership with a two-step transfer process." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership with a two-step transfer process. + + + +- Two-step ownership transfer for enhanced security. +- `owner()`, `pendingOwner()` view functions for state inspection. +- `renounceOwnership()` function to relinquish ownership. + + +## Overview + +The OwnerTwoStepsFacet provides a robust ownership management system for Compose diamonds. It enforces a two-step ownership transfer mechanism, requiring both the current owner to initiate a transfer and the new owner to accept it, enhancing security and preventing accidental ownership changes. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "@compose/contracts/facets/ownership/interfaces/IOwnerTwoStepsFacet.sol"; +import {DiamondProxy} from "@compose/contracts/core/DiamondProxy.sol"; + +contract OwnerUser { + IOwnerTwoStepsFacet ownerFacet; + + constructor(address diamondProxyAddress) { + ownerFacet = IOwnerTwoStepsFacet(diamondProxyAddress); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function initiateOwnershipTransfer(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function acceptNewOwnership() external { + ownerFacet.acceptOwnership(); + } + + function renounceCurrentOwnership() external { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize ownership transfers using `transferOwnership` and require the new owner to call `acceptOwnership` to complete the process. +- Only the current owner can initiate transfers or renounce ownership. +- The pending owner address is cleared after `acceptOwnership` or if the owner renounces. + + +## Security Considerations + + +Ensure that only authorized accounts can call `transferOwnership`, `acceptOwnership`, and `renounceOwnership`. The `OwnerUnauthorizedAccount` error is emitted if the caller is not the owner or pending owner as appropriate. Direct access to storage slots via `getOwnerStorage` and `getPendingOwnerStorage` should be used with caution and only by trusted facets. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..8d3388a3 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -0,0 +1,309 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "Manages ERC-173 two-step contract ownership." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-173 two-step contract ownership. + + + +- Implements a secure ERC-173 compliant two-step ownership transfer. +- Provides `owner()` and `pendingOwner()` view functions for state inspection. +- Includes a `requireOwner()` internal modifier for access control within facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module implements a secure, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are intentional by requiring explicit acceptance from the new owner, preventing accidental or malicious transfers. This pattern is crucial for maintaining control and upgradeability in a decentralized environment. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoSteps} from "../interfaces/IOwnerTwoSteps.sol"; + +contract MyOwnerFacet { + // Assuming OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are accessible + // and the diamond storage layout is correctly set up. + IOwnerTwoSteps private ownerTwoStepsFacet; + + function initialize(address _diamondAddress) public { + // Assuming IOwnerTwoSteps interface is registered with the diamond proxy + ownerTwoStepsFacet = IOwnerTwoSteps(_diamondAddress); + } + + function transferContractOwnership(address _newOwner) external { + // Call the transferOwnership function from the OwnerTwoSteps module + ownerTwoStepsFacet.transferOwnership(_newOwner); + } + + function acceptContractOwnership() external { + // Call the acceptOwnership function from the OwnerTwoSteps module + ownerTwoStepsFacet.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return ownerTwoStepsFacet.owner(); + } + + function getPendingOwner() external view returns (address) { + return ownerTwoStepsFacet.pendingOwner(); + } + + function renounceContractOwnership() external { + ownerTwoStepsFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Use `transferOwnership` to initiate transfers and require the new owner to call `acceptOwnership` to finalize. +- Implement `requireOwner` checks within your facet functions to restrict sensitive operations to the current owner. +- Be aware that `renounceOwnership` permanently removes owner privileges; use with extreme caution. + + +## Integration Notes + + +This module manages ownership state within its own designated storage slots. Facets interacting with ownership should use the provided `IOwnerTwoSteps` interface to call functions like `transferOwnership` and `acceptOwnership`. The `owner` and `pendingOwner` states are globally accessible through the diamond proxy via the `IOwnerTwoSteps` interface. Ensure that the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are correctly defined and not conflicting with other facets' storage. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json new file mode 100644 index 00000000..54acbd6c --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Two-Step Owner", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/access/OwnerTwoSteps", + "description": "Two-step ownership transfer pattern." + } +} diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json new file mode 100644 index 00000000..32cd8855 --- /dev/null +++ b/website/docs/library/access/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Access Control", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/access", + "description": "Access control patterns for permission management in Compose diamonds." + } +} diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx new file mode 100644 index 00000000..90187263 --- /dev/null +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -0,0 +1,422 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Manage diamond facets and functions programmatically." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and functions programmatically. + + + +- Supports adding, replacing, and removing functions and entire facets. +- Allows for optional execution of an initialization function during a cut operation. +- Provides granular control over the diamond's functional surface area. + + +## Overview + +The DiamondCutFacet provides the essential on-chain mechanism for upgrading and managing the functional surface area of a Compose diamond. It allows for the addition, replacement, and removal of functions across various facets, ensuring the diamond's capabilities can evolve over time. This facet is crucial for maintaining and extending the diamond's functionality post-deployment. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/contracts/facets/DiamondCut/IDiamondCut.sol"; + +contract Deployer { + // Assume diamondAddress is the address of your deployed diamond proxy + address diamondAddress; + + function upgradeDiamond() external { + // Get the DiamondCutFacet interface + IDiamondCut diamondCutFacet = IDiamondCut(diamondAddress); + + // Define facet cut data + // Example: Add a new ERC721 facet + address newErc721FacetAddress = address(0x123...); // Address of the deployed ERC721 facet contract + bytes4[] memory erc721Selectors = new bytes4[](2); + erc721Selectors[0] = IDiamondCut.getOwnerStorage.selector; // Example selector + erc721Selectors[1] = IDiamondCut.getDiamondStorage.selector; // Example selector + + // Execute the diamond cut + // Note: The owner must have permissions to call diamondCut + diamondCutFacet.diamondCut( + new IDiamondCut.FacetCut[](0), // No facets to remove + new IDiamondCut.FacetCut[](1){ \ + facetAddress: newErc721FacetAddress, + action: IDiamondCut.FacetCutAction.ADD, + selectors: erc721Selectors + }, + address(0), // No init function to call + bytes("") // No init data + ); + } +}`} + + +## Best Practices + + +- Ensure the caller has the necessary permissions (e.g., owner role) before invoking `diamondCut`. +- Carefully manage facet addresses and selector mappings to prevent unintended function overwrites or removals. +- Store facet deployment addresses off-chain or in a trusted registry for secure upgrades. + + +## Security Considerations + + +The `diamondCut` function is highly sensitive and should only be callable by authorized addresses. Incorrect usage can lead to loss of functionality or unintended state changes. Ensure all function selectors are correctly mapped to their corresponding facet addresses. Be cautious when replacing existing functions, especially immutable ones, as this can break existing integrations. Initialization functions executed during `diamondCut` must be carefully audited for reentrancy and other vulnerabilities. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx new file mode 100644 index 00000000..eae33e26 --- /dev/null +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -0,0 +1,396 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Manages diamond facet additions, removals, and replacements." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond facet additions, removals, and replacements. + + + +- Dynamically add, remove, or replace functions on the diamond proxy. +- Supports batch operations for multiple facet changes in a single transaction. +- Includes error handling for common issues like non-existent selectors or attempting to modify immutable functions. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCutMod provides essential functions for dynamically managing the facets attached to a Compose diamond. It allows for the addition of new functions, the removal of existing ones, and the replacement of functions with new implementations, all while ensuring the integrity and safety of the diamond's logic. This module is crucial for upgrading and evolving diamond functionality post-deployment. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/contracts/diamond/interfaces/IDiamondCut.sol"; +import {DiamondCutMod} from "@compose/contracts/diamond/modules/DiamondCutMod.sol"; + +contract MyDiamondFacet { + // Assume IDiamondCut is already implemented on the diamond + IDiamondCut internal diamondCut = IDiamondCut(address(this)); + + function upgradeMyFacet(address _newFacetAddress, bytes4[] memory _selectors) external { + // Example: Replacing functions + // Ensure _newFacetAddress is a valid facet contract + // Ensure _selectors are the functions to be replaced from the old facet + // and are present in the new facet. + diamondCut.diamondCut( + new IDiamondCut.FacetCut[]({ + IDiamondCut.FacetCut({ + facetAddress: _newFacetAddress, + action: IDiamondCut.FacetCutAction.Replace, + selectors: _selectors + }) + }), + address(0), // No init function + \"\" // No init data + ); + } + + function addNewFunctionality(address _newFacetAddress, bytes4[] memory _selectors) external { + diamondCut.diamondCut( + new IDiamondCut.FacetCut[]({ + IDiamondCut.FacetCut({ + facetAddress: _newFacetAddress, + action: IDiamondCut.FacetCutAction.Add, + selectors: _selectors + }) + }), + address(0), + \"\" + ); + } +}`} + + +## Best Practices + + +- Use `diamondCut` with `FacetCutAction.Replace` carefully, ensuring the new facet's selectors are compatible with the existing diamond logic to avoid breaking functionality. +- Always provide valid `selectors` when adding or replacing functions. An empty `selectors` array for `Add` actions will revert with `NoSelectorsProvidedForFacet`. +- Be aware of `Immutable` functions. Attempting to remove or replace them will revert with specific errors, preventing accidental modification of core diamond logic. + + +## Integration Notes + + +The `DiamondCutMod` interacts with the diamond's storage to manage the mapping of selectors to facet addresses. When functions are added, removed, or replaced, these changes are immediately reflected in the diamond's routing logic. Facets that interact with the diamond proxy should be aware that the underlying facet implementations can change. The `diamondCut` function can optionally execute an initialization function via `delegatecall` after the cut operation, allowing for state setup in new facets. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..27293508 --- /dev/null +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -0,0 +1,254 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "Query diamond facets, addresses, and function selectors." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Query diamond facets, addresses, and function selectors. + + + +- Provides a standardized interface for querying diamond components. +- Optimized for gas efficiency when querying large diamonds with many facets and selectors. +- Supports querying individual facet addresses, all facet addresses, and selectors per facet. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query which facets are registered, their associated addresses, and the function selectors they implement. This is crucial for understanding the diamond's structure, debugging, and building compatible extensions. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupe} from "@compose/diamond/facets/DiamondLoupe/IDiamondLoupe.sol"; + +contract DiamondConsumer { + IDiamondLoupe public diamondLoupeFacet; + + constructor(address _diamondAddress) { + diamondLoupeFacet = IDiamondLoupe(_diamondAddress); + } + + function getFacetAddresses() external view returns (address[] memory) { + return diamondLoupeFacet.facetAddresses(); + } + + function getFacetSelectors(address _facetAddress) external view returns (bytes4[] memory) { + return diamondLoupeFacet.facetFunctionSelectors(_facetAddress); + } + + function getAllFacets() external view returns (IDiamondLoupe.Facet[] memory) { + return diamondLoupeFacet.facets(); + } +}`} + + +## Best Practices + + +- Initialize the facet with the diamond's address to enable introspection. +- Use the returned data to verify diamond state or to dynamically route calls. +- Cache facet addresses and selectors locally if frequent querying is required to minimize on-chain calls. + + +## Security Considerations + + +This facet is primarily for read operations and does not directly manage state changes. Ensure that the diamond address provided during initialization is the correct one to prevent querying unintended contracts. The gas cost of extensive querying should be considered in gas-sensitive applications. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx new file mode 100644 index 00000000..17045aa2 --- /dev/null +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -0,0 +1,237 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Manage diamond facets and internal storage." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and internal storage. + + + +- Enables programmatic addition of facets and their function selectors during diamond deployment. +- Provides a secure mechanism (`getStorage`) to inspect the diamond's internal storage layout. +- Acts as the central point for function dispatch via `diamondFallback`, routing calls to the appropriate facet. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides essential internal functions for managing facets within a diamond proxy, including adding new facets and providing access to diamond storage. It is crucial for the diamond's initialization and runtime operation, ensuring facets are correctly registered and accessible. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondMod} from "@compose/contracts/diamond/IDiamondMod.sol"; + +contract MyFacet { + IDiamondMod internal diamondMod; + + constructor(address _diamondMod) { + diamondMod = IDiamondMod(_diamondMod); + } + + function addMyFacet() external { + // Example: Illustrative, actual facet registration is done at deployment. + // This function demonstrates calling an internal diamond function for context. + // diamondMod.addFacets(...); // This is typically called by the diamond deployer. + } + + function getDiamondStorage() external view returns (bytes memory) { + return diamondMod.getStorage(); + } +}`} + + +## Best Practices + + +- Facet addition is restricted to the diamond deployment phase to maintain integrity and predictability. +- Utilize `getStorage()` to safely access and inspect internal diamond storage state, ensuring no direct manipulation that could break invariants. +- Understand that `diamondFallback` is the core dispatch mechanism; ensure all facet functions are correctly registered to be discoverable. + + +## Integration Notes + + +The `DiamondMod` contract manages the diamond's core state, including the mapping of function selectors to facet addresses and the internal storage layout. Facets interact with `DiamondMod` primarily through the `diamondFallback` mechanism for function execution and `getStorage` for introspection. Changes to facet registrations via `addFacets` are typically performed only during the initial diamond deployment. + + +
+ +
+ + diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json new file mode 100644 index 00000000..423c02c9 --- /dev/null +++ b/website/docs/library/diamond/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Diamond Core", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/diamond", + "description": "Core diamond proxy functionality for ERC-2535 diamonds." + } +} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx new file mode 100644 index 00000000..0cab7692 --- /dev/null +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -0,0 +1,129 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Example Diamond contract for Compose framework" +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Example Diamond contract for Compose framework + + + +- Initializes diamond with facets and owner. +- Registers function selectors for delegatecall routing. +- Provides a basic structural example for Compose diamonds. + + +## Overview + +This contract serves as a foundational example for a Compose diamond. It demonstrates diamond initialization by registering facets and their function selectors, enabling delegatecall routing. It establishes ownership and sets up the initial diamond structure. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-diamond/diamond-cut/src/IDiamondCut.sol"; +import {ExampleDiamond} from "./ExampleDiamond.sol"; + +contract DeployExampleDiamond { + address public diamondAddress; + + function deploy() public { + // Define facets to be added + ExampleDiamond.FacetCut[] memory facets = new ExampleDiamond.FacetCut[](1); + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = ExampleDiamond.deploy.selector; // Assuming a function named 'deploy' exists in a facet + facets[0] = ExampleDiamond.FacetCut( + address(1), // Replace with actual facet address + ExampleDiamond.FacetCutAction.Add, + selectors + ); + + // Deploy the diamond and initialize it + diamondAddress = address(new ExampleDiamond(facets, msg.sender)); + } +}`} + + +## Best Practices + + +- Use explicit initializer functions for setting up diamond contracts and their facets. +- Ensure all facets are registered with their correct function selectors during deployment. +- Manage ownership and access control carefully, especially during initialization. + + +## Security Considerations + + +The `constructor` function is critical for setting up the diamond's initial state. Ensure facet addresses and selectors are accurate to prevent routing to unintended functions. Ownership is set in the constructor, so the `msg.sender` should be a trusted deployer. + + +
+ +
+ + diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json new file mode 100644 index 00000000..d6c0dc0d --- /dev/null +++ b/website/docs/library/diamond/example/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "example", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/diamond/example", + "description": "example components for Compose diamonds." + } +} diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx new file mode 100644 index 00000000..3f585714 --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -0,0 +1,157 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "Implements ERC-165 interface detection for diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements ERC-165 interface detection for diamonds. + + + +- Implements the ERC-165 standard for interface detection. +- Provides internal functions for registering and querying supported interfaces. +- Designed for seamless integration with the Compose diamond storage pattern. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod provides the necessary storage and internal functions to comply with the ERC-165 standard for interface detection. This allows diamonds and their facets to programmatically declare which interfaces they support, enhancing interoperability and discoverability within the Compose ecosystem. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC165, IERC165Mod} from "@compose/modules/erc165/LibERC165.sol"; +import {IDiamondCut} from "@compose/diamond/IDiamond.sol"; + +contract MyERC721Facet { + /** + * @notice Initializes the facet, registering ERC721 and ERC165 interfaces. + * @param _diamondCut Address of the DiamondCut facet for initialization. + */ + function initialize(IDiamondCut _diamondCut) external { + // ... other initialization logic ... + + // Register ERC721 and ERC165 support + LibERC165.registerInterface(type(IERC721).interfaceId); + LibERC165.registerInterface(type(IERC165).interfaceId); + } + + // ... other facet functions ... +}`} + + +## Best Practices + + +- Register supported interfaces during facet initialization using `LibERC165.registerInterface()`. +- Ensure the ERC165Mod is added to the diamond, typically as part of the diamond's base facets. +- Call `LibERC165.supportsInterface()` from facets or external contracts to check for interface support. + + +## Integration Notes + + +The ERC165Mod utilizes a dedicated storage slot to maintain a mapping of supported interface IDs. Facets can access this storage indirectly via the library functions. When adding the ERC165Mod as a facet, ensure its initialization function is called to register the interfaces supported by the diamond and its facets. The `supportsInterface` function is available externally via the diamond proxy to query interface support. + + +
+ +
+ + diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json new file mode 100644 index 00000000..2ed43f06 --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-165", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/interfaceDetection/ERC165", + "description": "ERC-165 components for Compose diamonds." + } +} diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json new file mode 100644 index 00000000..2126981f --- /dev/null +++ b/website/docs/library/interfaceDetection/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Interface Detection", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/interfaceDetection", + "description": "ERC-165 interface detection support." + } +} diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx new file mode 100644 index 00000000..5c52db22 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -0,0 +1,678 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "Manages ERC-1155 fungible and non-fungible tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 fungible and non-fungible tokens. + + + +- Supports both fungible and non-fungible tokens within a single facet. +- Implements batched transfer and balance checking functions for efficiency. +- Provides flexible URI management for token metadata. + + +## Overview + +The ERC1155Facet provides a comprehensive implementation for the ERC-1155 Multi-Token Standard within a Compose diamond. It handles token balances, approvals, and transfers for multiple token types, enabling both fungible and non-fungible assets to coexist and be managed efficiently through the diamond proxy. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Facet} from "@compose/contracts/facets/ERC1155/IERC1155Facet.sol"; +import {ERC1155FacetSelectors} from "@compose/contracts/facets/ERC1155/ERC1155FacetSelectors.sol"; + +contract MyDiamond is IDiamondCut { + // ... deployment logic ... + + function _diamondCut() internal override returns (FacetCut[] memory) { + // ... other facet cuts ... + return + abi.encodePacked( + FacetCut({ + facet: address(new ERC1155Facet()), + action: IDiamondCut.FacetCutAction.ADD, + selectors: ERC1155FacetSelectors.ALL + }) + ); + } + + function getERC1155Facet() public view returns (IERC1155Facet) { + return IERC1155Facet(address(this)); + } +} + +contract Consumer { + function getBalance(address diamond, address account, uint256 id) public view returns (uint256) { + return diamond.balanceOf(account, id); + } + + function getTokenURI(address diamond, uint256 id) public view returns (string memory) { + return diamond.uri(id); + } +}`} + + +## Best Practices + + +- Initialize the ERC1155Facet with appropriate base URI and token URIs during diamond deployment. +- Utilize `safeTransferFrom` and `safeBatchTransferFrom` for all token transfers to ensure adherence to the ERC-1155 standard. +- Manage approvals carefully using `setApprovalForAll` to control operator permissions. + + +## Security Considerations + + +Ensure that `safeTransferFrom` and `safeBatchTransferFrom` are used exclusively to prevent reentrancy issues and guarantee proper handling of token transfers. Verify that the caller has sufficient balance and necessary approvals before initiating transfers. Input validation for token IDs and amounts is crucial to prevent unexpected behavior or denial of service. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx new file mode 100644 index 00000000..f299b351 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -0,0 +1,605 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "Manages ERC-1155 token minting, burning, and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 token minting, burning, and transfers. + + + +- Supports both single and batch minting and burning of ERC-1155 tokens. +- Implements safe transfer logic for single and batch operations, including ERC1155Receiver validation. +- Manages token URIs with `setBaseURI` and `setTokenURI` functions, emitting `URI` events. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod provides core ERC-1155 token functionalities, including minting, burning, and safe transfers. It integrates seamlessly with the diamond storage pattern, allowing facets to manage token balances and metadata efficiently. This module ensures compliance with ERC-1155 standards for both single and batch operations. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Mod} from "@compose/modules/erc1155/IERC1155Mod.sol"; + +contract MyERC1155Facet { + // Assume diamond storage is accessible and ERC1155Mod is initialized + IERC1155Mod internal constant ERC1155Mod = IERC1155Mod(address(this)); + + function mintNewTokens(address _to, uint256 _id, uint256 _amount) external { + // Mint tokens to the recipient + ERC1155Mod.mint(_to, _id, _amount); + } + + function burnExistingTokens(address _from, uint256 _id, uint256 _amount) external { + // Burn tokens from the sender + ERC1155Mod.burn(_from, _id, _amount); + } + + function transferSomeTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + // Safely transfer tokens + ERC1155Mod.safeTransferFrom(_from, _to, _id, _amount, ""); + } +}`} + + +## Best Practices + + +- Ensure the ERC1155Mod is correctly initialized within the diamond's storage layout. +- Always validate `_to` and `_from` addresses before performing transfers or mints to prevent unexpected behavior. +- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, and `ERC1155MissingApprovalForAll` errors appropriately in your facet logic. + + +## Integration Notes + + +The ERC1155Mod operates on a dedicated storage slot within the diamond's global storage. Facets interacting with this module should use the `IERC1155Mod` interface to call its functions. Token balances and URI data are stored and managed by this module, and changes are immediately visible to all facets that have access to the diamond's storage. The `getStorage` function provides direct access to the module's internal storage struct. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json new file mode 100644 index 00000000..220e1ab9 --- /dev/null +++ b/website/docs/library/token/ERC1155/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-1155", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC1155", + "description": "ERC-1155 multi-token implementations." + } +} diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx new file mode 100644 index 00000000..c2c6c201 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -0,0 +1,256 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Burn ERC-20 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC-20 tokens within a Compose diamond. + + + +- Allows burning of ERC-20 tokens directly from user balances. +- Supports burning tokens from other accounts via allowances. +- Emits `Transfer` events to the zero address upon successful burning, adhering to ERC-20 standards. + + +## Overview + +The ERC20BurnFacet provides functionality to burn ERC-20 tokens directly within a Compose diamond. It enables users to reduce token supply by destroying their own tokens or tokens they have an allowance for, ensuring compliance with ERC-20 standards. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; + +contract ERC20BurnConsumer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnMyTokens(uint256 _amount) external { + // Get the ERC20BurnFacet interface + IERC20BurnFacet burnFacet = IERC20BurnFacet(diamondAddress); + + // Burn tokens from the caller's balance + burnFacet.burn(_amount); + } + + function burnTokensFromSomeone(address _from, uint256 _amount) external { + // Get the ERC20BurnFacet interface + IERC20BurnFacet burnFacet = IERC20BurnFacet(diamondAddress); + + // Burn tokens from another account using allowance + burnFacet.burnFrom(_from, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20BurnFacet is properly registered and accessible via the diamond proxy. +- Use `burnFrom` only after approving the necessary allowance to the diamond address. +- Handle `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` errors appropriately. + + +## Security Considerations + + +This facet relies on the underlying ERC-20 token contract for balance and allowance checks. Ensure the diamond's access control mechanisms are correctly configured to prevent unauthorized burning. The `burnFrom` function requires prior approval of an allowance, which should be managed carefully by token holders. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx new file mode 100644 index 00000000..6c3e1364 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -0,0 +1,564 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Standard ERC-20 token functionality for Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Standard ERC-20 token functionality for Compose diamonds. + + + +- Implements all standard ERC-20 functions (name, symbol, decimals, totalSupply, balanceOf, allowance, approve, transfer, transferFrom). +- Leverages the Compose Diamond storage pattern for state management. +- Emits standard ERC-20 `Transfer` and `Approval` events. + + +## Overview + +The ERC20Facet provides a complete implementation of the ERC-20 token standard, enabling fungible token operations within a Compose diamond. It exposes standard functions for querying token details, managing balances, and handling allowances and transfers, making it a fundamental building block for tokenized assets. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Facet} from "@compose-protocol/diamond-contracts/facets/ERC20/IERC20Facet.sol"; + +contract ERC20Consumer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function getTokenName() external view returns (string memory) { + IERC20Facet erc20Facet = IERC20Facet(diamondAddress); + return erc20Facet.name(); + } + + function transferTokens(address _to, uint256 _amount) external { + IERC20Facet erc20Facet = IERC20Facet(diamondAddress); + erc20Facet.transfer(_to, _amount); + } +}`} + + +## Best Practices + + +- Initialize the ERC20 storage struct correctly during diamond deployment or upgrade. +- Ensure proper access control is implemented at the diamond level for administrative functions if applicable (though standard ERC-20 functions are typically permissionless). +- Use `approve` before `transferFrom` to manage token spending permissions securely. + + +## Security Considerations + + +Standard ERC-20 security considerations apply. Ensure input validation for addresses and amounts. Be mindful of potential reentrancy if custom logic interacts with token transfers. The `approve` function can lead to unintended spending if not used carefully by users; consider implementing checks for zero allowance before approving if your application logic requires it. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx new file mode 100644 index 00000000..8e12b534 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -0,0 +1,425 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "ERC-20 token logic with core transfer, mint, and burn operations." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token logic with core transfer, mint, and burn operations. + + + +- Supports standard ERC-20 `transfer` and `transferFrom` operations. +- Implements `mint` for creating new tokens and `burn` for destroying tokens. +- Manages token `approvals` for delegated transfers. +- Provides a `getStorage` function for direct access to internal storage, enabling interoperability. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod module provides essential ERC-20 token functionality, including transfers, minting, and burning. It manages token balances and allowances, adhering to the ERC-20 standard. Integrating this module allows diamonds to support fungible tokens with standard on-chain operations. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "@compose/modules/erc20/IERC20Mod.sol"; +import { ERC20Mod } from "@compose/modules/erc20/ERC20Mod.sol"; + +contract MyDiamondFacet { + ERC20Mod internal erc20; + + // Assumes ERC20Mod is initialized and its storage slot is known + constructor(address _erc20StorageAddress) { + erc20 = ERC20Mod(_erc20StorageAddress); + } + + function transferTokens(address _to, uint256 _amount) external { + erc20.transfer(msg.sender, _to, _amount); + } + + function approveSpender(address _spender, uint256 _amount) external { + erc20.approve(msg.sender, _spender, _amount); + } + + function burnTokens(uint256 _amount) external { + erc20.burn(msg.sender, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20Mod facet is correctly initialized with the appropriate storage slot. +- Handle `ERC20InsufficientBalance`, `ERC20InsufficientAllowance`, `ERC20InvalidReceiver`, `ERC20InvalidSender`, and `ERC20InvalidSpender` errors to manage token operations gracefully. +- Be mindful of total supply changes when minting or burning tokens. + + +## Integration Notes + + +The ERC20Mod relies on a specific storage slot to maintain its state, including token balances, allowances, and total supply. Facets interacting with this module must either call its functions through the diamond proxy or directly access its storage via the `getStorage` function if they are integrated within the same diamond and understand the storage layout. Ensure that no other facets or modules overwrite the storage slot allocated for ERC20Mod, as this would lead to data corruption. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json new file mode 100644 index 00000000..c72db04e --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC20/ERC20", + "description": "ERC-20 fungible token implementations." + } +} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..a1cb3995 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -0,0 +1,417 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "Facilitates cross-chain ERC20 token bridging operations." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Facilitates cross-chain ERC20 token bridging operations. + + + +- Cross-chain minting and burning capabilities for ERC20 tokens. +- Role-based access control restricted to `trusted-bridge` addresses. +- Internal functions for retrieving storage and validating bridge authenticity. + + +## Overview + +The ERC20BridgeableFacet enables secure and controlled cross-chain transfers of ERC20 tokens. It provides functions for minting and burning tokens on behalf of trusted bridge operators, ensuring integrity and adherence to access control policies. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "@compose/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; + +contract Deployer { + address diamondAddress; + + function deploy() public { + // ... diamond deployment logic ... + diamondAddress = address(0xYourDiamondProxyAddress); + } + + function mintCrosschain(address _to, uint256 _amount) public { + IERC20BridgeableFacet bridgeFacet = IERC20BridgeableFacet(diamondAddress); + // Assuming caller has the 'trusted-bridge' role + bridgeFacet.crosschainMint(_to, _amount); + } + + function burnCrosschain(address _from, uint256 _amount) public { + IERC20BridgeableFacet bridgeFacet = IERC20BridgeableFacet(diamondAddress); + // Assuming caller has the 'trusted-bridge' role + bridgeFacet.crosschainBurn(_from, _amount); + } +}`} + + +## Best Practices + + +- Ensure only trusted addresses are granted the `trusted-bridge` role for cross-chain operations. +- Utilize `checkTokenBridge` internally or externally to verify bridge authorization before critical actions. +- Store ERC20 and access control configurations in designated storage slots for organized state management. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are permissioned and callable only by addresses holding the `trusted-bridge` role. The `checkTokenBridge` function enforces this role check. Ensure the `trusted-bridge` role is managed securely to prevent unauthorized token minting or burning. Reentrancy is not a concern as these functions do not make external calls. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..65d50973 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -0,0 +1,431 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "Manage cross-chain ERC20 token transfers and burns." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage cross-chain ERC20 token transfers and burns. + + + +- Cross-chain minting and burning of ERC20 tokens. +- Strict access control for bridge operations via the `trusted-bridge` role. +- Utilizes internal assembly for efficient storage access. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module enables secure cross-chain operations for ERC20 tokens. It allows trusted bridge addresses to mint or burn tokens on behalf of users, facilitating interoperability between different blockchain networks. Access is strictly controlled via an access control role. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableMod} from "@compose/contracts/src/modules/ERC20BridgeableMod.sol"; +import {IDiamondStorage} from "@compose/contracts/src/interfaces/IDiamondStorage.sol"; + +contract ERC20BridgeableFacet { + address immutable DIAMOND_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.compose.v1")))); + + function _getErc20BridgeableMod() internal view returns (IERC20BridgeableMod) { + return IERC20BridgeableMod(DIAMOND_STORAGE_SLOT); + } + + /** + * @notice Mints tokens cross-chain. + * @param _recipient The address to mint tokens to. + * @param _amount The amount of tokens to mint. + */ + function crosschainMint(address _recipient, uint256 _amount) external { + _getErc20BridgeableMod().crosschainMint(_recipient, _amount); + } + + /** + * @notice Burns tokens cross-chain. + * @param _burner The address burning tokens. + * @param _amount The amount of tokens to burn. + */ + function crosschainBurn(address _burner, uint256 _amount) external { + _getErc20BridgeableMod().crosschainBurn(_burner, _amount); + } +}`} + + +## Best Practices + + +- Ensure only addresses with the `trusted-bridge` role can call `crosschainMint` and `crosschainBurn`. +- Handle `AccessControlUnauthorizedAccount` and `ERC20InvalidBridgeAccount` errors appropriately in client applications. +- Be aware that token supply changes are managed by external trusted bridge accounts. + + +## Integration Notes + + +This module relies on the `AccessControl` and `ERC20` storage layouts within the diamond. The `getAccessControlStorage` and `getERC20Storage` helper functions provide direct access to these critical storage areas. The `trusted-bridge` role is defined within the `AccessControl` storage. Facets interacting with this module should use the provided helper functions to access storage to ensure consistency and compatibility. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json new file mode 100644 index 00000000..710c86dd --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-20 Bridgeable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC20/ERC20Bridgeable", + "description": "ERC-20 Bridgeable extension for ERC-20 tokens." + } +} diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx new file mode 100644 index 00000000..6a5c6ae8 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -0,0 +1,340 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "Manage ERC-20 token allowances with EIP-2612 permit functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-20 token allowances with EIP-2612 permit functionality. + + + +- Implements EIP-2612 `permit` function for gasless approvals. +- Provides `nonces` and `DOMAIN_SEPARATOR` to facilitate off-chain signature generation. +- Integrates seamlessly with the Compose diamond proxy pattern. + + +## Overview + +The ERC20PermitFacet enables EIP-2612 compliant on-chain signature-based approvals for ERC-20 tokens. This allows users to grant token allowances to spenders without needing to sign an explicit ERC-20 `approve` transaction, reducing gas costs and improving user experience. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit, ERC20PermitFacet} from "@compose/contracts/src/facets/ERC20PermitFacet.sol"; +import {IDiamondCut, DiamondInit} from "@compose/contracts/src/interfaces/IDiamond.sol"; + +contract MyDiamondInit is DiamondInit { + function init(IDiamondCut.FacetCut[] memory _diamondCut) public override { + // ... other initializations ... + + // Add ERC20PermitFacet + address erc20PermitFacetAddress = address(new ERC20PermitFacet()); + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut( + erc20PermitFacetAddress, + IDiamondCut.FacetCutAction.ADD, + IDiamondCut.getSelectors(erc20PermitFacetAddress) + ); + + // Assuming diamondCut is available from DiamondInit + super.init(cuts); + } +} + +contract UserInteraction { + ERC20PermitFacet public erc20PermitFacet; // Assume this is deployed and selectors added to diamond + IERC20Permit public token; // The ERC-20 token contract + + function grantPermit(address spender, uint256 amount, uint256 deadline, bytes calldata signature) public { + // User would have previously called nonces() and DOMAIN_SEPARATOR() to construct permit data + // and then signed it off-chain. + erc20PermitFacet.permit(token, spender, amount, deadline, signature); + } +}`} + + +## Best Practices + + +- Ensure the `ERC20PermitFacet` is correctly deployed and its selectors are added to the diamond's routing. +- Users must obtain the `DOMAIN_SEPARATOR` and their current `nonce` from the diamond before constructing and signing the permit message. +- Carefully manage the `deadline` parameter to prevent permits from expiring prematurely or remaining valid indefinitely. + + +## Security Considerations + + +The `permit` function is permissionless and directly modifies allowances. Ensure the signature is valid and the nonce is current to prevent replay attacks or unauthorized allowance changes. The `deadline` parameter is critical for limiting the validity of permits. Users must verify the `DOMAIN_SEPARATOR` to ensure they are signing for the correct contract and chain. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx new file mode 100644 index 00000000..40571140 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -0,0 +1,284 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "ERC-2612 Permit logic for token allowances." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-2612 Permit logic for token allowances. + + + +- Implements ERC-2612 Permit functionality, allowing off-chain signed allowances. +- Provides a standard `DOMAIN_SEPARATOR` for signature domain separation. +- Includes necessary storage for permit logic. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the necessary functions and storage structures to implement ERC-20 Permit (EIP-2612) functionality. It enables users to grant token allowances via signed messages, enhancing user experience by reducing the need for direct on-chain interactions for every allowance setting. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20PermitMod} from "@compose/modules/erc20/permit/IERC20PermitMod.sol"; + +contract MyTokenFacet { + IERC20PermitMod public immutable erc20PermitMod; + + constructor(address _erc20PermitModAddress) { + erc20PermitMod = IERC20PermitMod(_erc20PermitModAddress); + } + + /** + * @notice Sets an allowance for a spender using an ERC-2612 permit. + * @param owner The owner of the tokens. + * @param spender The address that will be allowed to spend the tokens. + * @param value The amount of tokens that the spender is allowed to spend. + * @param deadline The deadline for the permit. + * @param v The v component of the signature. + * @param r The r component of the signature. + * @param s The s component of the signature. + */ + function setAllowanceWithPermit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // The permit function emits the Approval event internally if successful. + erc20PermitMod.permit(owner, spender, value, deadline, v, r, s); + // Facet logic to update internal allowance tracking or other state can go here. + } +}`} + + +## Best Practices + + +- Ensure the `permit` function is called by a facet that correctly handles the `Approval` event emission as specified by the module. +- Implement robust signature verification logic within the facet calling the `permit` function if additional checks are required beyond the module's validation. +- Be mindful of the `deadline` parameter to prevent stale permit usage. + + +## Integration Notes + + +The `ERC20PermitMod` requires access to the diamond's storage to manage its internal state, including the domain separator and permit data. Facets interacting with this module should ensure they are correctly initialized with the module's address and that they correctly handle the `Approval` event emitted by the `permit` function. The `DOMAIN_SEPARATOR` is computed based on the diamond's address and chain ID, ensuring uniqueness and replay protection. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json new file mode 100644 index 00000000..2282a192 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-20 Permit", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC20/ERC20Permit", + "description": "ERC-20 Permit extension for ERC-20 tokens." + } +} diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json new file mode 100644 index 00000000..32857d9d --- /dev/null +++ b/website/docs/library/token/ERC20/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC20", + "description": "ERC-20 fungible token implementations." + } +} diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx new file mode 100644 index 00000000..4c0f3194 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -0,0 +1,525 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "Manage ERC-6909 compliant token balances and operator permissions." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-6909 compliant token balances and operator permissions. + + + +- Implements core ERC-6909 functionality for token management. +- Supports transfer, transferFrom, approve, and setOperator operations. +- Provides view functions for querying balances, allowances, and operator status. +- Emits standard ERC-6909 events for state changes. + + +## Overview + +The ERC6909Facet provides functionality for managing fungible or non-fungible token balances and operator approvals according to the ERC-6909 standard. It enables transfers, balance queries, allowance checks, and operator management directly within the diamond proxy. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Facet} from "@compose-protocol/diamond/contracts/facets/ERC6909/IERC6909Facet.sol"; +import {DiamondProxy} from "@compose-protocol/diamond/contracts/DiamondProxy.sol"; + +contract ERC6909Consumer { + IERC6909Facet public erc6909Facet; + + constructor(address _diamondProxyAddress) { + erc6909Facet = IERC6909Facet(_diamondProxyAddress); + } + + function consumeERC6909(address _receiver, uint256 _amount) external { + // Assume token ID is known or managed elsewhere + uint256 tokenId = 1; + erc6909Facet.transfer(tokenId, msg.sender, _receiver, _amount); + } + + function getBalance(address _owner, uint256 _tokenId) external view returns (uint256) { + return erc6909Facet.balanceOf(_owner, _tokenId); + } +}`} + + +## Best Practices + + +- Integrate the `ERC6909Facet` into your diamond via deployment scripts. Ensure the facet's functions are correctly added to the diamond's facet registry. +- Use the `balanceOf`, `allowance`, and `isOperator` functions for read-only queries to understand token ownership and permissions. +- Handle `Transfer` and `Approval` events emitted by the facet to maintain off-chain state or trigger further actions. + + +## Security Considerations + + +Ensure appropriate access control is configured at the diamond level for functions like `transfer`, `transferFrom`, and `approve` if they are not intended to be universally accessible. Validate receiver and sender addresses to prevent sending tokens to unintended recipients. Be mindful of potential reentrancy if downstream logic triggered by events is not carefully implemented. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx new file mode 100644 index 00000000..3f593c41 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -0,0 +1,518 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "ERC-6909 minimal multi-token logic and storage." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-6909 minimal multi-token logic and storage. + + + +- Implements core ERC-6909 functions: `transfer`, `approve`, `mint`, `burn`, `setOperator`. +- Manages token balances and allowances for multiple token IDs. +- Supports operator functionality for delegated token management. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic and storage for implementing the ERC-6909 standard, enabling minimal multi-token functionality within a Compose diamond. It handles token approvals, transfers, minting, and burning, abstracting complex state management into a single, composable unit. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod} from "@compose/modules/ERC6909Mod.sol"; +import {IDiamond} from "@compose/core/IDiamond.sol"; + +contract MyERC6909Facet { + IERC6909Mod private constant ERC6909 = IERC6909Mod(address(this)); + + function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + ERC6909.transfer(_from, _to, _id, _amount); + } + + function approveSpender(address _spender, uint256 _id, uint256 _amount) external { + ERC6909.approve(_spender, _id, _amount); + } + + function mintNewTokens(address _to, uint256 _id, uint256 _amount) external { + ERC6909.mint(_to, _id, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC6909Mod facet is correctly initialized with the appropriate storage slot. +- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` in calling facets. +- Be aware that operator permissions grant broad access to token balances; manage operator roles carefully. + + +## Integration Notes + + +The ERC6909Mod module utilizes a specific storage slot (defined by `STORAGE_POSITION`) for its internal `ERC6909Storage` struct. Facets interacting with this module should use the `getStorage` function to access the storage pointer. This ensures that all ERC-6909 operations correctly reference and modify the shared state, maintaining atomicity and consistency across the diamond. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json new file mode 100644 index 00000000..bff34320 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC6909/ERC6909", + "description": "ERC-6909 minimal multi-token implementations." + } +} diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json new file mode 100644 index 00000000..aba18a4d --- /dev/null +++ b/website/docs/library/token/ERC6909/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC6909", + "description": "ERC-6909 minimal multi-token implementations." + } +} diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx new file mode 100644 index 00000000..45885d39 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -0,0 +1,212 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "Burn ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens within a Compose diamond. + + + +- Destroys ERC721 tokens, removing them from circulation and enumeration. +- Leverages the diamond storage pattern for efficient access to token data. +- Emits `Transfer` and `ApprovalForAll` events to signal token destruction. + + +## Overview + +The ERC721BurnFacet allows for the destruction of ERC721 tokens managed by a Compose diamond. It integrates with the diamond's storage pattern to access and modify token ownership and enumeration data, ensuring that burned tokens are permanently removed from the system. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; + +// Assume ERC721BurnFacet and DiamondInit are deployed and cut into the diamond + +// Example of burning a token +contract BurnExample { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnToken(uint256 tokenId) public { + // Select the burn function from the ERC721BurnFacet + // The selector for burn is 0x18d739c1 + (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(bytes4(0x18d739c1), tokenId)); + require(success, "Burn failed"); + } +}`} + + +## Best Practices + + +- Ensure the ERC721BurnFacet is correctly cut into the diamond proxy with appropriate selectors. +- Verify that the `owner` of the token or an `operator` approved for the token has sufficient approval to burn the token, as enforced by the underlying ERC721 logic. + + +## Security Considerations + + +This facet relies on the underlying ERC721 implementation for ownership and approval checks. Ensure that the caller has the necessary permissions (owner or approved operator) to burn the specified token. The `burn` function does not perform reentrancy checks; ensure the caller is not a malicious contract designed for reentrancy attacks. Input validation for `tokenId` is handled by the underlying ERC721 logic. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx new file mode 100644 index 00000000..c65cc6bb --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -0,0 +1,664 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "Manages ERC-721 token ownership, transfers, and approvals." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-721 token ownership, transfers, and approvals. + + + +- Implements core ERC-721 functions including name, symbol, balanceOf, ownerOf, and tokenURI. +- Supports token approvals via `approve` and `setApprovalForAll`. +- Includes internal and external transfer functions for flexible token movement. + + +## Overview + +The ERC721Facet provides the core functionality for managing non-fungible tokens (NFTs) within a Compose diamond. It handles token ownership, retrieval of token metadata, and manages approvals for token transfers, enabling composable NFT logic. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose-protocol/diamond/contracts/facets/ERC721/IERC721Facet.sol"; + +contract NftManager { + IERC721Facet private immutable _erc721Facet; + + constructor(address _diamondAddress) { + _erc721Facet = IERC721Facet(_diamondAddress); + } + + function getNftName() external view returns (string memory) { + return _erc721Facet.name(); + } + + function getNftSymbol() external view returns (string memory) { + return _erc721Facet.symbol(); + } + + function getTokenOwner(uint256 tokenId) external view returns (address) { + return _erc721Facet.ownerOf(tokenId); + } + + function transferMyToken(address to, uint256 tokenId) external { + _erc721Facet.transferFrom(msg.sender, to, tokenId); + } +}`} + + +## Best Practices + + +- Initialize the diamond with the ERC721Facet to enable NFT functionality. +- Ensure correct ownership and approval checks are performed before attempting transfers. +- Use `safeTransferFrom` for transfers to unknown or untrusted recipient contracts. + + +## Security Considerations + + +Input validation is critical for all functions to prevent errors and potential exploits. Ensure that `ownerOf` checks are performed before transfers and that approvals are properly managed. Reentrancy is mitigated by the diamond proxy pattern and internal function design. Use of `safeTransferFrom` is recommended when interacting with potentially untrusted recipient contracts. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx new file mode 100644 index 00000000..b2969345 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -0,0 +1,358 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "Internal logic for ERC-721 token management." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Internal logic for ERC-721 token management. + + + +- Provides atomic operations for minting, transferring, and burning ERC-721 tokens. +- Manages ERC-721 token ownership and approvals within diamond storage. +- Abstracts complex ERC-721 state management into reusable internal logic. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod provides core internal logic for managing ERC-721 tokens within a Compose diamond. It encapsulates the state and operations for minting, transferring, burning, and managing approvals, ensuring consistency and reusability across different ERC-721 facets. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod } from "@compose/modules/erc721/IERC721Mod.sol"; +import {ERC721ModStorage} from "@compose/modules/erc721/ERC721ModStorage.sol"; + +contract MyERC721Facet { + IERC721Mod public immutable erc721Mod; + + constructor(address _diamondAddress) { + erc721Mod = IERC721Mod(_diamondAddress); + } + + function safeMint(address _to, uint256 _tokenId) external { + // Assume ownership checks are handled by access control + ERC721ModStorage.ERC721Storage storage erc721Storage = erc721Mod.getStorage(); + erc721Mod.mint(_to, _tokenId); + } + + function safeTransfer(address _from, address _to, uint256 _tokenId) external { + // Assume ownership and approval checks are handled internally by transferFrom + erc721Mod.transferFrom(_from, _to, _tokenId); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented in facets calling these internal functions. +- Handle ERC721Mod custom errors (`ERC721IncorrectOwner`, `ERC721NonexistentToken`, etc.) in calling facets. +- Be aware that `setMetadata` is not described and may require explicit implementation in a facet. + + +## Integration Notes + + +The ERC721Mod utilizes a predefined storage slot for its `ERC721Storage` struct. Facets integrating with this module can access this storage directly via the `getStorage()` function. Any modifications made to the ERC-721 state through the module's functions (e.g., `mint`, `transferFrom`, `burn`) are persistent and visible across all facets interacting with the diamond's ERC-721 functionality. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json new file mode 100644 index 00000000..57484cb7 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC721/ERC721", + "description": "ERC-721 non-fungible token implementations." + } +} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..43948de7 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,221 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "Handles burning of ERC721 tokens and maintains enumeration." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Handles burning of ERC721 tokens and maintains enumeration. + + + +- Enables burning of ERC721 tokens. +- Maintains internal token enumeration consistency after burns. +- Emits `Transfer` event for burned tokens (from owner to address(0)). + + +## Overview + +The ERC721EnumerableBurnFacet provides the functionality to burn ERC721 tokens within a Compose diamond. It ensures that token destruction is properly recorded and that the internal enumeration of tokens remains accurate after a burn operation. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurnFacet} from "@compose-protocol/diamond/facets/ERC721/IERC721EnumerableBurnFacet.sol"; + +contract ExampleUsage { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function burnToken(uint256 _tokenId) external { + // Assume appropriate access control is handled by the diamond proxy + IERC721EnumerableBurnFacet(diamondAddress).burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the diamond proxy is configured to route `burn` calls to this facet. +- Implement necessary access control mechanisms within the diamond to authorize token burning. + + +## Security Considerations + + +Access control for burning tokens must be strictly enforced at the diamond proxy level. The `burn` function relies on the caller having the necessary permissions to burn the specified token. The facet itself does not implement specific access control checks beyond those inherent to ERC721 ownership and approvals. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..85e76a9d --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -0,0 +1,739 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "Enumerable ERC-721 token management" +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enumerable ERC-721 token management + + + +- Provides `totalSupply`, `balanceOf`, and `ownerOf` for standard ERC-721 queries. +- Enables iteration over an owner's tokens using `tokenOfOwnerByIndex`. +- Supports standard ERC-721 approval and transfer mechanisms, including safe transfers. + + +## Overview + +This facet provides full ERC-721 enumerable functionality, allowing querying of token metadata, ownership details, and total supply. It extends the standard ERC-721 interface by enabling iteration over tokens owned by an address and tracking token ownership by index. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableFacet} from "@compose/diamond/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; + +contract ERC721EnumerableConsumer { + IERC721EnumerableFacet private immutable _erc721; + + constructor(address diamondAddress) { + _erc721 = IERC721EnumerableFacet(diamondAddress); + } + + function getERC721Name() external view returns (string memory) { + return _erc721.name(); + } + + function getTokenOwner(uint256 tokenId) external view returns (address) { + return _erc721.ownerOf(tokenId); + } + + function getTokensOwnedBy(address owner) external view returns (uint256[] memory) { + uint256 count = _erc721.balanceOf(owner); + uint256[] memory tokenIds = new uint256[](count); + for (uint256 i = 0; i < count; i++) { + tokenIds[i] = _erc721.tokenOfOwnerByIndex(owner, i); + } + return tokenIds; + } +}`} + + +## Best Practices + + +- Initialize the diamond with this facet to enable ERC-721 enumerable features. +- Use `tokenOfOwnerByIndex` carefully, as it requires knowledge of the owner's token balance to iterate effectively. +- Ensure that any custom ERC-721 implementations correctly integrate with the internal transfer logic to maintain enumerable state. + + +## Security Considerations + + +Access control for `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` functions must be rigorously enforced by the diamond's access control mechanism. The `internalTransferFrom` function is a critical internal component; ensure it is only called by authorized functions to prevent state manipulation. Reentrancy is a concern for transfer functions; ensure proper checks are in place, especially for `safeTransferFrom` when interacting with external contracts. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..e1ced973 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -0,0 +1,344 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "Manages enumerable ERC721 tokens within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages enumerable ERC721 tokens within a diamond. + + + +- Manages the addition and removal of tokens from enumeration lists during mint and burn operations. +- Provides internal functions for minting, burning, and transferring ERC721 tokens while maintaining enumeration state. +- Utilizes inline assembly via `getStorage` to access its internal storage struct efficiently. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic for managing enumerable ERC721 tokens. It ensures that minted tokens are added to internal tracking lists and burned tokens are removed, maintaining accurate token counts and ownership history. Integrating this module allows facets to seamlessly implement ERC721 functionality with enumeration support. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC721EnumerableMod} from "./ERC721EnumerableMod.sol"; + +contract MyERC721Facet { + ERC721EnumerableMod internal enumerableMod; + + function initialize(address _diamondAddress) external { + // Assuming ERC721EnumerableMod is deployed and its address is known + // In a real scenario, this address would likely be passed in or retrieved from a registry. + address enumerableModAddress = _diamondAddress; // Placeholder + enumerableMod = ERC721EnumerableMod(enumerableModAddress); + } + + function mintToken(address _to, uint256 _tokenId) external { + enumerableMod.mint(_to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + enumerableMod.burn(_tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + enumerableMod.transferFrom(_from, _to, _tokenId); + } +}`} + + +## Best Practices + + +- Ensure proper authorization checks are implemented in facets calling `mint`, `burn`, and `transferFrom` functions. +- Handle custom errors like `ERC721NonexistentToken`, `ERC721InvalidSender`, and `ERC721InvalidReceiver` in calling facets for robust error management. +- Be aware of the storage slot used by `ERC721EnumerableMod` and avoid conflicts when adding other facets. + + +## Integration Notes + + +The `ERC721EnumerableMod` relies on a predefined storage slot for its `ERC721EnumerableStorage` struct. Facets integrating this module must ensure that this slot is not overwritten by other facets. The `getStorage` function uses inline assembly to directly access this storage slot, making the module's state available to any facet that calls it. Changes to the enumeration lists are immediately reflected in the diamond's state. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json new file mode 100644 index 00000000..c80a4b77 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-721 Enumerable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC721/ERC721Enumerable", + "description": "ERC-721 Enumerable extension for ERC-721 tokens." + } +} diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json new file mode 100644 index 00000000..208a6407 --- /dev/null +++ b/website/docs/library/token/ERC721/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/ERC721", + "description": "ERC-721 non-fungible token implementations." + } +} diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx new file mode 100644 index 00000000..77914fee --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -0,0 +1,188 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "Manages and retrieves royalty information per token." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages and retrieves royalty information per token. + + + +- Implements ERC-2981 `royaltyInfo` standard. +- Supports default and token-specific royalty configurations. +- Calculates royalty amounts based on sale price and basis points. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, allowing Compose diamonds to specify and retrieve royalty details for token sales. It supports both default royalties and token-specific overrides, ensuring proper distribution of sale proceeds. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyFacet} from "@compose/diamond/facets/Royalty/IRoyaltyFacet.sol"; + +contract RoyaltyConsumer { + address immutable diamondProxy; + bytes4 constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; + + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; + } + + function getSaleRoyalty(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 royaltyAmount) { + (receiver, royaltyAmount) = IRoyaltyFacet(diamondProxy).royaltyInfo(tokenId, salePrice); + } +}`} + + +## Best Practices + + +- Initialize default royalties during diamond deployment or via an admin function. +- Token-specific royalties should be managed by the NFT contract facet, calling into this facet's setter functions if available (or directly setting storage if access is granted). +- Ensure the `STORAGE_POSITION` for royalty data is correctly defined and unique. + + +## Security Considerations + + +Access to modify royalty settings should be restricted to authorized roles (e.g., owner, admin) to prevent malicious manipulation of royalty distributions. Ensure input validation for `tokenId` and `salePrice` is handled appropriately by calling facets. The `getStorage` function uses inline assembly; verify the `STORAGE_POSITION` is correctly set to avoid data corruption. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx new file mode 100644 index 00000000..138c6ace --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -0,0 +1,382 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "Manages ERC-2981 royalties with default and token-specific settings." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-2981 royalties with default and token-specific settings. + + + +- Supports ERC-2981 standard for on-chain royalties. +- Allows setting both default and token-specific royalty configurations. +- Provides fallback logic from token-specific to default royalties. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides robust ERC-2981 royalty management, enabling both default royalty settings and token-specific overrides. It ensures compatibility with the ERC-2981 standard by implementing the `royaltyInfo` function, which queries token-specific or fallback default royalty information. This composable approach allows diamonds to easily integrate royalty logic without complex inheritance. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "path/to/IRoyaltyMod.sol"; + +contract RoyaltyFacet { + // Assume IRoyaltyMod is imported and diamond storage is accessed correctly + // For example, via a diamond storage contract. + IRoyaltyMod internal royaltyMod; + + constructor(address _diamondAddress) { + // Initialize royaltyMod with the diamond address + // This is a simplified example; actual initialization depends on diamond proxy setup. + royaltyMod = IRoyaltyMod(_diamondAddress); + } + + /** + * @notice Sets a default royalty for all tokens. + * @param _receiver The address to receive royalty payments. + * @param _feeBasisPoints The royalty fee in basis points (e.g., 100 for 1%). + */ + function setDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setDefaultRoyalty(_receiver, _feeBasisPoints); + } + + /** + * @notice Sets a specific royalty for a token ID. + * @param _tokenId The ID of the token to set royalty for. + * @param _receiver The address to receive royalty payments. + * @param _feeBasisPoints The royalty fee in basis points. + */ + function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Queries royalty information for a given token ID and sale price. + * @param _tokenId The ID of the token. + * @param _salePrice The sale price of the token. + * @return receiver The address to receive the royalty payment. + * @return royaltyAmount The calculated royalty amount. + */ + function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { + (receiver, royaltyAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); + return (receiver, royaltyAmount); + } + + /** + * @notice Resets royalty information for a specific token to use the default. + * @param _tokenId The ID of the token to reset. + */ + function resetTokenRoyalty(uint256 _tokenId) external { + royaltyMod.resetTokenRoyalty(_tokenId); + } + + /** + * @notice Deletes the default royalty information. + */ + function deleteDefaultRoyalty() external { + royaltyMod.deleteDefaultRoyalty(); + } +} +`} + + +## Best Practices + + +- Validate receiver addresses and fee basis points to prevent unexpected royalty distributions using the module's custom errors. +- Use `resetTokenRoyalty` to revert token-specific royalties to the default, ensuring predictable fallback behavior. +- Be aware that calling `deleteDefaultRoyalty` will cause `royaltyInfo` to return `(address(0), 0)` for tokens without specific royalties. + + +## Integration Notes + + +This module interacts with diamond storage at a predefined slot to manage royalty data. Facets can access this data using the `getStorage` function, which uses inline assembly to read from the diamond's storage. Changes to default or token-specific royalties are immediately reflected in subsequent calls to `royaltyInfo`. Ensure that the storage slot for royalty data is not utilized by other facets to avoid conflicts. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json new file mode 100644 index 00000000..615503af --- /dev/null +++ b/website/docs/library/token/Royalty/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Royalty", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token/Royalty", + "description": "ERC-2981 royalty standard implementations." + } +} diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json new file mode 100644 index 00000000..7eca87aa --- /dev/null +++ b/website/docs/library/token/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Token Standards", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/token", + "description": "Token standard implementations for Compose diamonds." + } +} diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx new file mode 100644 index 00000000..cfc0b343 --- /dev/null +++ b/website/docs/library/utils/NonReentrancyMod.mdx @@ -0,0 +1,137 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "Prevent reentrant calls within facets." +gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Prevent reentrant calls within facets. + + + +- Provides `enter()` and `exit()` functions to manage reentrancy guards. +- Utilizes a simple `uint256` as storage for the reentrancy state, allowing for composability. +- Emits a `Reentrancy` error if a reentrant call is detected. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides essential functions to prevent reentrant function calls within your diamond facets. By managing an internal state, it ensures that a function cannot be re-entered before its initial execution completes, safeguarding against unexpected state changes and potential exploits. + +--- + +## Storage + +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/modules/NonReentrancyMod.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 internal _reentrancyState; + + /** + * @notice Performs an action that must not be reentrant. + */ + function performAction() external { + _reentrancyState.enter(); + + // ... perform sensitive operations ... + + _reentrancyState.exit(); + } +}`} + + +## Best Practices + + +- Always pair `enter()` with a corresponding `exit()` call, ideally using `try...finally` or ensuring `exit()` is called even if internal operations revert. +- Place `enter()` at the very beginning of the function and `exit()` at the very end to establish the widest possible protection. +- Consider the implications of reentrancy on all external calls made within a protected function. + + +## Integration Notes + + +The `NonReentrancyMod` expects to manage its state within a `uint256` slot. Facets integrating this module should ensure that a `uint256` is allocated in their storage layout for the reentrancy state. The `enter` and `exit` functions operate directly on this `uint256` slot, making it a self-contained and composable guard. + + +
+ +
+ + diff --git a/website/docs/library/utils/_category_.json b/website/docs/library/utils/_category_.json new file mode 100644 index 00000000..b01ae58a --- /dev/null +++ b/website/docs/library/utils/_category_.json @@ -0,0 +1,11 @@ +{ + "label": "Utilities", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "generated-index", + "slug": "/docs/library/utils", + "description": "Utility libraries and helpers for diamond development." + } +} From 106f91d54d50ae398ce37d3532bc988095380a10 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 16:23:19 -0500 Subject: [PATCH 47/68] improve category idx pages --- .../generate-docs-utils/category-generator.js | 104 ++- .../category/category-generator.js | 624 ++++++++++++++++++ .../category/index-page-generator.js | 208 ++++++ .../doc-generation-utils.js | 2 +- .../index-page-generator.js | 208 ++++++ .github/scripts/sync-docs-structure.js | 2 +- 6 files changed, 1137 insertions(+), 11 deletions(-) create mode 100644 .github/scripts/generate-docs-utils/category/category-generator.js create mode 100644 .github/scripts/generate-docs-utils/category/index-page-generator.js create mode 100644 .github/scripts/generate-docs-utils/index-page-generator.js diff --git a/.github/scripts/generate-docs-utils/category-generator.js b/.github/scripts/generate-docs-utils/category-generator.js index 229e015e..39028181 100644 --- a/.github/scripts/generate-docs-utils/category-generator.js +++ b/.github/scripts/generate-docs-utils/category-generator.js @@ -14,6 +14,10 @@ const fs = require('fs'); const path = require('path'); const CONFIG = require('./config'); +const { + getCategoryItems, + createCategoryIndexFile: createIndexFile, +} = require('./category/index-page-generator'); // ============================================================================ // Constants @@ -301,6 +305,27 @@ function computeSlug(outputDir, libraryDir) { return `/docs/library/${normalizedPath}`; } +/** + * Wrapper function to create category index file using the index-page-generator utility + * @param {string} outputDir - Directory to create index file in + * @param {string} relativePath - Relative path from library dir + * @param {string} label - Category label + * @param {string} description - Category description + * @param {boolean} overwrite - Whether to overwrite existing files (default: false) + * @returns {boolean} True if file was created/updated, false if skipped + */ +function createCategoryIndexFile(outputDir, relativePath, label, description, overwrite = false) { + return createIndexFile( + outputDir, + relativePath, + label, + description, + generateLabel, + generateDescription, + overwrite + ); +} + /** * Create a _category_.json file for a directory * @param {string} outputDir - Directory to create category file in @@ -325,17 +350,21 @@ function createCategoryFile(outputDir, name, relativePath, depth) { const label = generateLabel(actualDirName); const position = getCategoryPosition(actualDirName, depth); const description = generateDescription(actualDirName, parentParts); - const slug = computeSlug(outputDir, libraryDir); + + // Create index.mdx file first + createCategoryIndexFile(outputDir, relativePath, label, description); + // Create category file pointing to index.mdx + const docId = relativePath ? `library/${relativePath}/index` : 'library/index'; + const category = { label, position, collapsible: true, collapsed: true, // Collapse all categories by default link: { - type: 'generated-index', - slug, - description, + type: 'doc', + id: docId, }, }; @@ -358,16 +387,20 @@ function ensureBaseCategory(libraryDir) { return false; } + const label = 'Library'; + const description = 'API reference for all Compose modules and facets.'; + + // Create index.mdx for base library category + createCategoryIndexFile(libraryDir, '', label, description, false); + const baseCategory = { - label: 'Library', + label, position: 4, collapsible: true, collapsed: true, // Collapse base Library category by default link: { - type: 'generated-index', - slug: '/docs/library', - title: 'Library Reference', - description: 'API reference for all Compose modules and facets.', + type: 'doc', + id: 'library/index', }, }; @@ -456,6 +489,57 @@ function ensureCategoryFiles(outputDir) { // Structure Synchronization // ============================================================================ +/** + * Regenerate index.mdx files for all categories + * @param {boolean} overwrite - Whether to overwrite existing files (default: true) + * @returns {object} Summary of regenerated categories + */ +function regenerateAllIndexFiles(overwrite = true) { + const structure = scanSourceStructure(); + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; + + const regenerated = []; + const skipped = []; + + // Regenerate base library index + const label = 'Library'; + const description = 'API reference for all Compose modules and facets.'; + if (createCategoryIndexFile(libraryDir, '', label, description, overwrite)) { + regenerated.push('library'); + } else { + skipped.push('library'); + } + + // Regenerate index for each category + const sortedPaths = Array.from(structure.entries()).sort((a, b) => + a[0].localeCompare(b[0]) + ); + + for (const [relativePath, info] of sortedPaths) { + const pathParts = relativePath.split('/'); + const mappedPathParts = pathParts.map(part => mapDirectoryName(part)); + const mappedRelativePath = mappedPathParts.join('/'); + const outputDir = path.join(libraryDir, ...mappedPathParts); + + const actualDirName = path.basename(outputDir); + const parentParts = mappedRelativePath.split('/').slice(0, -1); + const label = generateLabel(actualDirName); + const description = generateDescription(actualDirName, parentParts); + + if (createCategoryIndexFile(outputDir, mappedRelativePath, label, description, overwrite)) { + regenerated.push(mappedRelativePath); + } else { + skipped.push(mappedRelativePath); + } + } + + return { + regenerated, + skipped, + total: structure.size + 1, // +1 for base library + }; +} + /** * Synchronize docs structure with src structure * Creates any missing category directories and _category_.json files @@ -521,6 +605,8 @@ module.exports = { syncDocsStructure, computeOutputPath, ensureCategoryFiles, + createCategoryIndexFile, + regenerateAllIndexFiles, // Utilities generateLabel, diff --git a/.github/scripts/generate-docs-utils/category/category-generator.js b/.github/scripts/generate-docs-utils/category/category-generator.js new file mode 100644 index 00000000..977e57d0 --- /dev/null +++ b/.github/scripts/generate-docs-utils/category/category-generator.js @@ -0,0 +1,624 @@ +/** + * Category Generator + * + * Automatically generates _category_.json files to mirror + * the src/ folder structure in the documentation. + * + * This module provides: + * - Source structure scanning + * - Category file generation + * - Path computation for doc output + * - Structure synchronization + */ + +const fs = require('fs'); +const path = require('path'); +const CONFIG = require('../config'); +const { + getCategoryItems, + createCategoryIndexFile: createIndexFile, +} = require('./index-page-generator'); + +// ============================================================================ +// Constants +// ============================================================================ + +/** + * Human-readable labels for directory names + * Add new entries here when adding new top-level categories + */ +const CATEGORY_LABELS = { + // Top-level categories + access: 'Access Control', + token: 'Token Standards', + diamond: 'Diamond Core', + libraries: 'Utilities', + utils: 'Utilities', + interfaceDetection: 'Interface Detection', + + // Token subcategories + ERC20: 'ERC-20', + ERC721: 'ERC-721', + ERC1155: 'ERC-1155', + ERC6909: 'ERC-6909', + Royalty: 'Royalty', + + // Access subcategories + AccessControl: 'Access Control', + AccessControlPausable: 'Pausable Access Control', + AccessControlTemporal: 'Temporal Access Control', + Owner: 'Owner', + OwnerTwoSteps: 'Two-Step Owner', +}; + +/** + * Descriptions for categories + * Add new entries here for custom descriptions + */ +const CATEGORY_DESCRIPTIONS = { + // Top-level categories + access: 'Access control patterns for permission management in Compose diamonds.', + token: 'Token standard implementations for Compose diamonds.', + diamond: 'Core diamond proxy functionality for ERC-2535 diamonds.', + libraries: 'Utility libraries and helpers for diamond development.', + utils: 'Utility libraries and helpers for diamond development.', + interfaceDetection: 'ERC-165 interface detection support.', + + // Token subcategories + ERC20: 'ERC-20 fungible token implementations.', + ERC721: 'ERC-721 non-fungible token implementations.', + ERC1155: 'ERC-1155 multi-token implementations.', + ERC6909: 'ERC-6909 minimal multi-token implementations.', + Royalty: 'ERC-2981 royalty standard implementations.', + + // Access subcategories + AccessControl: 'Role-based access control (RBAC) pattern.', + AccessControlPausable: 'RBAC with pause functionality.', + AccessControlTemporal: 'Time-limited role-based access control.', + Owner: 'Single-owner access control pattern.', + OwnerTwoSteps: 'Two-step ownership transfer pattern.', +}; + +/** + * Sidebar positions for categories + * Lower numbers appear first in the sidebar + */ +const CATEGORY_POSITIONS = { + // Top-level (lower = higher priority) + diamond: 1, + access: 2, + token: 3, + libraries: 4, + utils: 4, + interfaceDetection: 5, + + // Token subcategories + ERC20: 1, + ERC721: 2, + ERC1155: 3, + ERC6909: 4, + Royalty: 5, + + // Access subcategories + Owner: 1, + OwnerTwoSteps: 2, + AccessControl: 3, + AccessControlPausable: 4, + AccessControlTemporal: 5, + + // Leaf directories (ERC20/ERC20, etc.) - alphabetical + ERC20Bridgeable: 2, + ERC20Permit: 3, + ERC721Enumerable: 2, +}; + +// ============================================================================ +// Label & Description Generation +// ============================================================================ + +/** + * Generate a human-readable label from a directory name + * @param {string} name - Directory name (e.g., 'AccessControlPausable', 'ERC20') + * @returns {string} Human-readable label + */ +function generateLabel(name) { + // Check explicit mapping first + if (CATEGORY_LABELS[name]) { + return CATEGORY_LABELS[name]; + } + + // Handle ERC standards specially + if (/^ERC\d+/.test(name)) { + const match = name.match(/^(ERC)(\d+)(.*)$/); + if (match) { + const variant = match[3] + ? ' ' + match[3].replace(/([A-Z])/g, ' $1').trim() + : ''; + return `ERC-${match[2]}${variant}`; + } + return name; + } + + // CamelCase to Title Case with spaces + return name.replace(/([A-Z])/g, ' $1').replace(/^ /, '').trim(); +} + +/** + * Generate description for a category based on its path + * @param {string} name - Directory name + * @param {string[]} parentPath - Parent path segments + * @returns {string} Category description + */ +function generateDescription(name, parentPath = []) { + // Check explicit mapping first + if (CATEGORY_DESCRIPTIONS[name]) { + return CATEGORY_DESCRIPTIONS[name]; + } + + // Generate from context + const label = generateLabel(name); + const parent = parentPath[parentPath.length - 1]; + + if (parent === 'token') { + return `${label} token implementations with modules and facets.`; + } + if (parent === 'access') { + return `${label} access control pattern for Compose diamonds.`; + } + if (parent === 'ERC20' || parent === 'ERC721') { + return `${label} extension for ${generateLabel(parent)} tokens.`; + } + + return `${label} components for Compose diamonds.`; +} + +/** + * Get sidebar position for a category + * @param {string} name - Directory name + * @param {number} depth - Nesting depth + * @returns {number} Sidebar position + */ +function getCategoryPosition(name, depth) { + if (CATEGORY_POSITIONS[name] !== undefined) { + return CATEGORY_POSITIONS[name]; + } + return 99; // Default to end +} + +// ============================================================================ +// Source Structure Scanning +// ============================================================================ + +/** + * Check if a directory contains .sol files (directly or in subdirectories) + * @param {string} dirPath - Directory path to check + * @returns {boolean} True if contains .sol files + */ +function containsSolFiles(dirPath) { + try { + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.sol')) { + return true; + } + if (entry.isDirectory() && !entry.name.startsWith('.')) { + if (containsSolFiles(path.join(dirPath, entry.name))) { + return true; + } + } + } + } catch (error) { + console.warn(`Warning: Could not read directory ${dirPath}: ${error.message}`); + } + + return false; +} + +/** + * Scan the src/ directory and build structure map + * @returns {Map} Map of relative paths to category info + */ +function scanSourceStructure() { + const srcDir = CONFIG.srcDir || 'src'; + const structure = new Map(); + + function scanDir(dirPath, relativePath = '') { + let entries; + try { + entries = fs.readdirSync(dirPath, { withFileTypes: true }); + } catch (error) { + console.error(`Error reading directory ${dirPath}: ${error.message}`); + return; + } + + for (const entry of entries) { + if (!entry.isDirectory()) continue; + + // Skip hidden directories and interfaces + if (entry.name.startsWith('.') || entry.name === 'interfaces') { + continue; + } + + const fullPath = path.join(dirPath, entry.name); + const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name; + + // Only include directories that contain .sol files + if (containsSolFiles(fullPath)) { + const parts = relPath.split('/'); + structure.set(relPath, { + name: entry.name, + path: relPath, + depth: parts.length, + parent: relativePath || null, + parentParts: relativePath ? relativePath.split('/') : [], + }); + + // Recurse into subdirectories + scanDir(fullPath, relPath); + } + } + } + + if (fs.existsSync(srcDir)) { + scanDir(srcDir); + } else { + console.warn(`Warning: Source directory ${srcDir} does not exist`); + } + + return structure; +} + +// ============================================================================ +// Category File Generation +// ============================================================================ + +/** + * Map source directory name to docs directory name + * @param {string} srcName - Source directory name + * @returns {string} Documentation directory name + */ +function mapDirectoryName(srcName) { + // Map libraries -> utils for URL consistency + if (srcName === 'libraries') { + return 'utils'; + } + return srcName; +} + +/** + * Compute slug from output directory path + * @param {string} outputDir - Full output directory path + * @param {string} libraryDir - Base library directory + * @returns {string} Slug path (e.g., '/docs/library/access') + */ +function computeSlug(outputDir, libraryDir) { + const relativePath = path.relative(libraryDir, outputDir); + + if (!relativePath || relativePath.startsWith('..')) { + // Root library directory + return '/docs/library'; + } + + // Convert path separators and create slug + const normalizedPath = relativePath.replace(/\\/g, '/'); + return `/docs/library/${normalizedPath}`; +} + +/** + * Wrapper function to create category index file using the index-page-generator utility + * @param {string} outputDir - Directory to create index file in + * @param {string} relativePath - Relative path from library dir + * @param {string} label - Category label + * @param {string} description - Category description + * @param {boolean} overwrite - Whether to overwrite existing files (default: false) + * @returns {boolean} True if file was created/updated, false if skipped + */ +function createCategoryIndexFile(outputDir, relativePath, label, description, overwrite = false) { + return createIndexFile( + outputDir, + relativePath, + label, + description, + generateLabel, + generateDescription, + overwrite + ); +} + +/** + * Create a _category_.json file for a directory + * @param {string} outputDir - Directory to create category file in + * @param {string} name - Directory name + * @param {string} relativePath - Relative path from library dir + * @param {number} depth - Nesting depth + * @returns {boolean} True if file was created, false if it already existed + */ +function createCategoryFile(outputDir, name, relativePath, depth) { + const categoryFile = path.join(outputDir, '_category_.json'); + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; + + // Don't overwrite existing category files (allows manual customization) + if (fs.existsSync(categoryFile)) { + return false; + } + + // Get the actual directory name from the output path (may be mapped, e.g., utils instead of libraries) + const actualDirName = path.basename(outputDir); + const parentParts = relativePath.split('/').slice(0, -1); + // Use actual directory name for label generation (supports both original and mapped names) + const label = generateLabel(actualDirName); + const position = getCategoryPosition(actualDirName, depth); + const description = generateDescription(actualDirName, parentParts); + + // Create index.mdx file first + createCategoryIndexFile(outputDir, relativePath, label, description); + + // Create category file pointing to index.mdx + const docId = relativePath ? `library/${relativePath}/index` : 'library/index'; + + const category = { + label, + position, + collapsible: true, + collapsed: true, // Collapse all categories by default + link: { + type: 'doc', + id: docId, + }, + }; + + // Ensure directory exists + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(categoryFile, JSON.stringify(category, null, 2) + '\n'); + + return true; +} + +/** + * Ensure the base library category file exists + * @param {string} libraryDir - Path to library directory + * @returns {boolean} True if created, false if existed + */ +function ensureBaseCategory(libraryDir) { + const categoryFile = path.join(libraryDir, '_category_.json'); + + if (fs.existsSync(categoryFile)) { + return false; + } + + const label = 'Library'; + const description = 'API reference for all Compose modules and facets.'; + + // Create index.mdx for base library category + createCategoryIndexFile(libraryDir, '', label, description, false); + + const baseCategory = { + label, + position: 4, + collapsible: true, + collapsed: true, // Collapse base Library category by default + link: { + type: 'doc', + id: 'library/index', + }, + }; + + fs.mkdirSync(libraryDir, { recursive: true }); + fs.writeFileSync(categoryFile, JSON.stringify(baseCategory, null, 2) + '\n'); + + return true; +} + +// ============================================================================ +// Path Computation +// ============================================================================ + +/** + * Compute output path for a source file + * Mirrors the src/ structure in website/docs/library/ + * Applies directory name mapping (e.g., libraries -> utils) + * + * @param {string} solFilePath - Path to .sol file (e.g., 'src/access/AccessControl/AccessControlMod.sol') + * @returns {object} Output path information + */ +function computeOutputPath(solFilePath) { + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; + + // Normalize path separators + const normalizedPath = solFilePath.replace(/\\/g, '/'); + + // Remove 'src/' prefix and '.sol' extension + const relativePath = normalizedPath.replace(/^src\//, '').replace(/\.sol$/, ''); + + const parts = relativePath.split('/'); + const fileName = parts.pop(); + + // Map directory names (e.g., libraries -> utils) + const mappedParts = parts.map(part => mapDirectoryName(part)); + + const outputDir = path.join(libraryDir, ...mappedParts); + const outputFile = path.join(outputDir, `${fileName}.mdx`); + + return { + outputDir, + outputFile, + relativePath: mappedParts.join('/'), + fileName, + category: mappedParts[0] || '', + subcategory: mappedParts[1] || '', + fullRelativePath: mappedParts.join('/'), + depth: mappedParts.length, + }; +} + +/** + * Ensure all parent category files exist for a given output path + * Creates _category_.json files for each directory level + * + * @param {string} outputDir - Full output directory path + */ +function ensureCategoryFiles(outputDir) { + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; + + // Get relative path from library base + const relativePath = path.relative(libraryDir, outputDir); + + if (!relativePath || relativePath.startsWith('..')) { + return; // outputDir is not under libraryDir + } + + // Ensure base category exists + ensureBaseCategory(libraryDir); + + // Walk up the directory tree, creating category files + const parts = relativePath.split(path.sep); + let currentPath = libraryDir; + + for (let i = 0; i < parts.length; i++) { + currentPath = path.join(currentPath, parts[i]); + const segment = parts[i]; + // Use the mapped path for the relative path (already mapped in computeOutputPath) + const relPath = parts.slice(0, i + 1).join('/'); + + createCategoryFile(currentPath, segment, relPath, i + 1); + } +} + +// ============================================================================ +// Structure Synchronization +// ============================================================================ + +/** + * Regenerate index.mdx files for all categories + * @param {boolean} overwrite - Whether to overwrite existing files (default: true) + * @returns {object} Summary of regenerated categories + */ +function regenerateAllIndexFiles(overwrite = true) { + const structure = scanSourceStructure(); + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; + + const regenerated = []; + const skipped = []; + + // Regenerate base library index + const label = 'Library'; + const description = 'API reference for all Compose modules and facets.'; + if (createCategoryIndexFile(libraryDir, '', label, description, overwrite)) { + regenerated.push('library'); + } else { + skipped.push('library'); + } + + // Regenerate index for each category + const sortedPaths = Array.from(structure.entries()).sort((a, b) => + a[0].localeCompare(b[0]) + ); + + for (const [relativePath, info] of sortedPaths) { + const pathParts = relativePath.split('/'); + const mappedPathParts = pathParts.map(part => mapDirectoryName(part)); + const mappedRelativePath = mappedPathParts.join('/'); + const outputDir = path.join(libraryDir, ...mappedPathParts); + + const actualDirName = path.basename(outputDir); + const parentParts = mappedRelativePath.split('/').slice(0, -1); + const label = generateLabel(actualDirName); + const description = generateDescription(actualDirName, parentParts); + + if (createCategoryIndexFile(outputDir, mappedRelativePath, label, description, overwrite)) { + regenerated.push(mappedRelativePath); + } else { + skipped.push(mappedRelativePath); + } + } + + return { + regenerated, + skipped, + total: structure.size + 1, // +1 for base library + }; +} + +/** + * Synchronize docs structure with src structure + * Creates any missing category directories and _category_.json files + * + * @returns {object} Summary of created categories + */ +function syncDocsStructure() { + const structure = scanSourceStructure(); + const libraryDir = CONFIG.libraryOutputDir || 'website/docs/library'; + + const created = []; + const existing = []; + + // Ensure base library directory exists with category + if (ensureBaseCategory(libraryDir)) { + created.push('library'); + } else { + existing.push('library'); + } + + // Create category for each directory in the structure + // Sort by path to ensure parents are created before children + const sortedPaths = Array.from(structure.entries()).sort((a, b) => + a[0].localeCompare(b[0]) + ); + + for (const [relativePath, info] of sortedPaths) { + // Map directory names in the path (e.g., libraries -> utils) + const pathParts = relativePath.split('/'); + const mappedPathParts = pathParts.map(part => mapDirectoryName(part)); + const mappedRelativePath = mappedPathParts.join('/'); + const outputDir = path.join(libraryDir, ...mappedPathParts); + + const wasCreated = createCategoryFile( + outputDir, + info.name, + mappedRelativePath, + info.depth + ); + + if (wasCreated) { + created.push(mappedRelativePath); + } else { + existing.push(mappedRelativePath); + } + } + + return { + created, + existing, + total: structure.size, + structure, + }; +} + +// ============================================================================ +// Exports +// ============================================================================ + +module.exports = { + // Core functions + scanSourceStructure, + syncDocsStructure, + computeOutputPath, + ensureCategoryFiles, + createCategoryIndexFile, + regenerateAllIndexFiles, + + // Utilities + generateLabel, + generateDescription, + getCategoryPosition, + containsSolFiles, + mapDirectoryName, + computeSlug, + + // For extending/customizing + CATEGORY_LABELS, + CATEGORY_DESCRIPTIONS, + CATEGORY_POSITIONS, +}; + diff --git a/.github/scripts/generate-docs-utils/category/index-page-generator.js b/.github/scripts/generate-docs-utils/category/index-page-generator.js new file mode 100644 index 00000000..cfa401e0 --- /dev/null +++ b/.github/scripts/generate-docs-utils/category/index-page-generator.js @@ -0,0 +1,208 @@ +/** + * Index Page Generator + * + * Generates index.mdx files for category directories with custom DocCard components. + * This module provides utilities for creating styled category index pages. + */ + +const fs = require('fs'); +const path = require('path'); +const CONFIG = require('../config'); + +// ============================================================================ +// Category Items Discovery +// ============================================================================ + +/** + * Get all items (documents and subcategories) in a directory + * @param {string} outputDir - Directory to scan + * @param {string} relativePath - Relative path from library dir + * @param {Function} generateLabel - Function to generate labels from names + * @param {Function} generateDescription - Function to generate descriptions + * @returns {Array} Array of items with type, name, label, href, description + */ +function getCategoryItems(outputDir, relativePath, generateLabel, generateDescription) { + const items = []; + + if (!fs.existsSync(outputDir)) { + return items; + } + + const entries = fs.readdirSync(outputDir, { withFileTypes: true }); + + for (const entry of entries) { + // Skip hidden files, category files, and index files + if (entry.name.startsWith('.') || + entry.name === '_category_.json' || + entry.name === 'index.mdx') { + continue; + } + + if (entry.isFile() && entry.name.endsWith('.mdx')) { + // It's a document + const docName = entry.name.replace('.mdx', ''); + const docPath = path.join(outputDir, entry.name); + + // Try to read frontmatter for title and description + let title = generateLabel(docName); + let description = ''; + + try { + const content = fs.readFileSync(docPath, 'utf8'); + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (frontmatterMatch) { + const frontmatter = frontmatterMatch[1]; + const titleMatch = frontmatter.match(/^title:\s*["']?(.*?)["']?$/m); + const descMatch = frontmatter.match(/^description:\s*["']?(.*?)["']?$/m); + if (titleMatch) title = titleMatch[1].trim(); + if (descMatch) description = descMatch[1].trim(); + } + } catch (error) { + // If reading fails, use defaults + } + + const docRelativePath = relativePath ? `${relativePath}/${docName}` : docName; + items.push({ + type: 'doc', + name: docName, + label: title, + description: description, + href: `/docs/library/${docRelativePath}`, + }); + } else if (entry.isDirectory()) { + // It's a subcategory + const subcategoryName = entry.name; + const subcategoryLabel = generateLabel(subcategoryName); + const subcategoryRelativePath = relativePath ? `${relativePath}/${subcategoryName}` : subcategoryName; + const subcategoryDescription = generateDescription(subcategoryName, relativePath.split('/')); + + items.push({ + type: 'category', + name: subcategoryName, + label: subcategoryLabel, + description: subcategoryDescription, + href: `/docs/library/${subcategoryRelativePath}`, + }); + } + } + + // Sort items: categories first, then docs, both alphabetically + items.sort((a, b) => { + if (a.type !== b.type) { + return a.type === 'category' ? -1 : 1; + } + return a.label.localeCompare(b.label); + }); + + return items; +} + +// ============================================================================ +// MDX Content Generation +// ============================================================================ + +/** + * Generate MDX content for a category index page + * @param {string} label - Category label + * @param {string} description - Category description + * @param {Array} items - Array of items to display + * @returns {string} Generated MDX content + */ +function generateIndexMdxContent(label, description, items) { + // Escape quotes in label and description for frontmatter + const escapedLabel = label.replace(/"/g, '\\"'); + const escapedDescription = description.replace(/"/g, '\\"'); + + let mdxContent = `--- +title: "${escapedLabel}" +description: "${escapedDescription}" +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ${escapedDescription} + + +`; + + if (items.length > 0) { + mdxContent += `\n`; + + for (const item of items) { + const iconName = item.type === 'category' ? 'package' : 'book'; + const itemDescription = item.description ? `"${item.description.replace(/"/g, '\\"')}"` : '""'; + + mdxContent += ` } + size="medium" + />\n`; + } + + mdxContent += `\n`; + } else { + mdxContent += `_No items in this category yet._\n`; + } + + return mdxContent; +} + +// ============================================================================ +// Index File Creation +// ============================================================================ + +/** + * Generate index.mdx file for a category + * @param {string} outputDir - Directory to create index file in + * @param {string} relativePath - Relative path from library dir + * @param {string} label - Category label + * @param {string} description - Category description + * @param {Function} generateLabel - Function to generate labels from names + * @param {Function} generateDescription - Function to generate descriptions + * @param {boolean} overwrite - Whether to overwrite existing files (default: false) + * @returns {boolean} True if file was created/updated, false if skipped + */ +function createCategoryIndexFile( + outputDir, + relativePath, + label, + description, + generateLabel, + generateDescription, + overwrite = false +) { + const indexFile = path.join(outputDir, 'index.mdx'); + + // Don't overwrite existing index files unless explicitly requested (allows manual customization) + if (!overwrite && fs.existsSync(indexFile)) { + return false; + } + + // Get items in this category + const items = getCategoryItems(outputDir, relativePath, generateLabel, generateDescription); + + // Generate MDX content + const mdxContent = generateIndexMdxContent(label, description, items); + + // Ensure directory exists + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(indexFile, mdxContent); + + return true; +} + +// ============================================================================ +// Exports +// ============================================================================ + +module.exports = { + getCategoryItems, + generateIndexMdxContent, + createCategoryIndexFile, +}; + diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js index 8d76f7e1..9c2cf42e 100644 --- a/.github/scripts/generate-docs-utils/doc-generation-utils.js +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -16,7 +16,7 @@ const CONFIG = require('./config'); const { computeOutputPath, ensureCategoryFiles, -} = require('./category-generator'); +} = require('./category/category-generator'); // ============================================================================ // Git Integration diff --git a/.github/scripts/generate-docs-utils/index-page-generator.js b/.github/scripts/generate-docs-utils/index-page-generator.js new file mode 100644 index 00000000..cfa401e0 --- /dev/null +++ b/.github/scripts/generate-docs-utils/index-page-generator.js @@ -0,0 +1,208 @@ +/** + * Index Page Generator + * + * Generates index.mdx files for category directories with custom DocCard components. + * This module provides utilities for creating styled category index pages. + */ + +const fs = require('fs'); +const path = require('path'); +const CONFIG = require('../config'); + +// ============================================================================ +// Category Items Discovery +// ============================================================================ + +/** + * Get all items (documents and subcategories) in a directory + * @param {string} outputDir - Directory to scan + * @param {string} relativePath - Relative path from library dir + * @param {Function} generateLabel - Function to generate labels from names + * @param {Function} generateDescription - Function to generate descriptions + * @returns {Array} Array of items with type, name, label, href, description + */ +function getCategoryItems(outputDir, relativePath, generateLabel, generateDescription) { + const items = []; + + if (!fs.existsSync(outputDir)) { + return items; + } + + const entries = fs.readdirSync(outputDir, { withFileTypes: true }); + + for (const entry of entries) { + // Skip hidden files, category files, and index files + if (entry.name.startsWith('.') || + entry.name === '_category_.json' || + entry.name === 'index.mdx') { + continue; + } + + if (entry.isFile() && entry.name.endsWith('.mdx')) { + // It's a document + const docName = entry.name.replace('.mdx', ''); + const docPath = path.join(outputDir, entry.name); + + // Try to read frontmatter for title and description + let title = generateLabel(docName); + let description = ''; + + try { + const content = fs.readFileSync(docPath, 'utf8'); + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (frontmatterMatch) { + const frontmatter = frontmatterMatch[1]; + const titleMatch = frontmatter.match(/^title:\s*["']?(.*?)["']?$/m); + const descMatch = frontmatter.match(/^description:\s*["']?(.*?)["']?$/m); + if (titleMatch) title = titleMatch[1].trim(); + if (descMatch) description = descMatch[1].trim(); + } + } catch (error) { + // If reading fails, use defaults + } + + const docRelativePath = relativePath ? `${relativePath}/${docName}` : docName; + items.push({ + type: 'doc', + name: docName, + label: title, + description: description, + href: `/docs/library/${docRelativePath}`, + }); + } else if (entry.isDirectory()) { + // It's a subcategory + const subcategoryName = entry.name; + const subcategoryLabel = generateLabel(subcategoryName); + const subcategoryRelativePath = relativePath ? `${relativePath}/${subcategoryName}` : subcategoryName; + const subcategoryDescription = generateDescription(subcategoryName, relativePath.split('/')); + + items.push({ + type: 'category', + name: subcategoryName, + label: subcategoryLabel, + description: subcategoryDescription, + href: `/docs/library/${subcategoryRelativePath}`, + }); + } + } + + // Sort items: categories first, then docs, both alphabetically + items.sort((a, b) => { + if (a.type !== b.type) { + return a.type === 'category' ? -1 : 1; + } + return a.label.localeCompare(b.label); + }); + + return items; +} + +// ============================================================================ +// MDX Content Generation +// ============================================================================ + +/** + * Generate MDX content for a category index page + * @param {string} label - Category label + * @param {string} description - Category description + * @param {Array} items - Array of items to display + * @returns {string} Generated MDX content + */ +function generateIndexMdxContent(label, description, items) { + // Escape quotes in label and description for frontmatter + const escapedLabel = label.replace(/"/g, '\\"'); + const escapedDescription = description.replace(/"/g, '\\"'); + + let mdxContent = `--- +title: "${escapedLabel}" +description: "${escapedDescription}" +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ${escapedDescription} + + +`; + + if (items.length > 0) { + mdxContent += `\n`; + + for (const item of items) { + const iconName = item.type === 'category' ? 'package' : 'book'; + const itemDescription = item.description ? `"${item.description.replace(/"/g, '\\"')}"` : '""'; + + mdxContent += ` } + size="medium" + />\n`; + } + + mdxContent += `\n`; + } else { + mdxContent += `_No items in this category yet._\n`; + } + + return mdxContent; +} + +// ============================================================================ +// Index File Creation +// ============================================================================ + +/** + * Generate index.mdx file for a category + * @param {string} outputDir - Directory to create index file in + * @param {string} relativePath - Relative path from library dir + * @param {string} label - Category label + * @param {string} description - Category description + * @param {Function} generateLabel - Function to generate labels from names + * @param {Function} generateDescription - Function to generate descriptions + * @param {boolean} overwrite - Whether to overwrite existing files (default: false) + * @returns {boolean} True if file was created/updated, false if skipped + */ +function createCategoryIndexFile( + outputDir, + relativePath, + label, + description, + generateLabel, + generateDescription, + overwrite = false +) { + const indexFile = path.join(outputDir, 'index.mdx'); + + // Don't overwrite existing index files unless explicitly requested (allows manual customization) + if (!overwrite && fs.existsSync(indexFile)) { + return false; + } + + // Get items in this category + const items = getCategoryItems(outputDir, relativePath, generateLabel, generateDescription); + + // Generate MDX content + const mdxContent = generateIndexMdxContent(label, description, items); + + // Ensure directory exists + fs.mkdirSync(outputDir, { recursive: true }); + fs.writeFileSync(indexFile, mdxContent); + + return true; +} + +// ============================================================================ +// Exports +// ============================================================================ + +module.exports = { + getCategoryItems, + generateIndexMdxContent, + createCategoryIndexFile, +}; + diff --git a/.github/scripts/sync-docs-structure.js b/.github/scripts/sync-docs-structure.js index c1bf36b9..4b4f833d 100644 --- a/.github/scripts/sync-docs-structure.js +++ b/.github/scripts/sync-docs-structure.js @@ -25,7 +25,7 @@ const path = require('path'); const scriptDir = __dirname; process.chdir(path.join(scriptDir, '../..')); -const { syncDocsStructure, scanSourceStructure } = require('./generate-docs-utils/category-generator'); +const { syncDocsStructure, scanSourceStructure } = require('./generate-docs-utils/category/category-generator'); // ============================================================================ // CLI Parsing From fc8e8677d3c3fe32a459f173c58ec546dfcb9e13 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 16:23:37 -0500 Subject: [PATCH 48/68] remove lib pages --- website/docs/library/_category_.json | 12 - .../AccessControl/AccessControlFacet.mdx | 554 ------------- .../access/AccessControl/AccessControlMod.mdx | 443 ----------- .../access/AccessControl/_category_.json | 11 - .../AccessControlPausableFacet.mdx | 397 ---------- .../AccessControlPausableMod.mdx | 379 --------- .../AccessControlPausable/_category_.json | 11 - .../AccessControlTemporalFacet.mdx | 461 ----------- .../AccessControlTemporalMod.mdx | 477 ----------- .../AccessControlTemporal/_category_.json | 11 - .../docs/library/access/Owner/OwnerFacet.mdx | 211 ----- .../docs/library/access/Owner/OwnerMod.mdx | 258 ------ .../docs/library/access/Owner/_category_.json | 11 - .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 291 ------- .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 309 -------- .../access/OwnerTwoSteps/_category_.json | 11 - website/docs/library/access/_category_.json | 11 - .../docs/library/diamond/DiamondCutFacet.mdx | 422 ---------- .../docs/library/diamond/DiamondCutMod.mdx | 396 ---------- .../library/diamond/DiamondLoupeFacet.mdx | 254 ------ website/docs/library/diamond/DiamondMod.mdx | 237 ------ website/docs/library/diamond/_category_.json | 11 - .../diamond/example/ExampleDiamond.mdx | 129 --- .../library/diamond/example/_category_.json | 11 - .../interfaceDetection/ERC165/ERC165Mod.mdx | 157 ---- .../interfaceDetection/ERC165/_category_.json | 11 - .../interfaceDetection/_category_.json | 11 - .../library/token/ERC1155/ERC1155Facet.mdx | 678 ---------------- .../docs/library/token/ERC1155/ERC1155Mod.mdx | 605 -------------- .../library/token/ERC1155/_category_.json | 11 - .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 256 ------ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 564 ------------- .../library/token/ERC20/ERC20/ERC20Mod.mdx | 425 ---------- .../library/token/ERC20/ERC20/_category_.json | 11 - .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 417 ---------- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 431 ---------- .../ERC20/ERC20Bridgeable/_category_.json | 11 - .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 340 -------- .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 284 ------- .../token/ERC20/ERC20Permit/_category_.json | 11 - .../docs/library/token/ERC20/_category_.json | 11 - .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 525 ------------- .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 518 ------------ .../token/ERC6909/ERC6909/_category_.json | 11 - .../library/token/ERC6909/_category_.json | 11 - .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 212 ----- .../token/ERC721/ERC721/ERC721Facet.mdx | 664 ---------------- .../library/token/ERC721/ERC721/ERC721Mod.mdx | 358 --------- .../token/ERC721/ERC721/_category_.json | 11 - .../ERC721EnumerableBurnFacet.mdx | 221 ------ .../ERC721EnumerableFacet.mdx | 739 ------------------ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 344 -------- .../ERC721/ERC721Enumerable/_category_.json | 11 - .../docs/library/token/ERC721/_category_.json | 11 - .../library/token/Royalty/RoyaltyFacet.mdx | 188 ----- .../docs/library/token/Royalty/RoyaltyMod.mdx | 382 --------- .../library/token/Royalty/_category_.json | 11 - website/docs/library/token/_category_.json | 11 - .../docs/library/utils/NonReentrancyMod.mdx | 137 ---- website/docs/library/utils/_category_.json | 11 - 60 files changed, 13928 deletions(-) delete mode 100644 website/docs/library/_category_.json delete mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx delete mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx delete mode 100644 website/docs/library/access/AccessControl/_category_.json delete mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/_category_.json delete mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json delete mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx delete mode 100644 website/docs/library/access/Owner/OwnerMod.mdx delete mode 100644 website/docs/library/access/Owner/_category_.json delete mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json delete mode 100644 website/docs/library/access/_category_.json delete mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondCutMod.mdx delete mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondMod.mdx delete mode 100644 website/docs/library/diamond/_category_.json delete mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx delete mode 100644 website/docs/library/diamond/example/_category_.json delete mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx delete mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json delete mode 100644 website/docs/library/interfaceDetection/_category_.json delete mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx delete mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx delete mode 100644 website/docs/library/token/ERC1155/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json delete mode 100644 website/docs/library/token/ERC20/_category_.json delete mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json delete mode 100644 website/docs/library/token/ERC6909/_category_.json delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json delete mode 100644 website/docs/library/token/ERC721/_category_.json delete mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx delete mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx delete mode 100644 website/docs/library/token/Royalty/_category_.json delete mode 100644 website/docs/library/token/_category_.json delete mode 100644 website/docs/library/utils/NonReentrancyMod.mdx delete mode 100644 website/docs/library/utils/_category_.json diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json deleted file mode 100644 index 720acf5e..00000000 --- a/website/docs/library/_category_.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "label": "Library", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library", - "title": "Library Reference", - "description": "API reference for all Compose modules and facets." - } -} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx deleted file mode 100644 index 6ac1d29e..00000000 --- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx +++ /dev/null @@ -1,554 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Manages roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages roles and permissions within a diamond. - - - -- Hierarchical role management: roles can have admin roles. -- Batch operations for granting and revoking roles. -- Explicit revert reasons for unauthorized access. - - -## Overview - -The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows defining roles, assigning them to addresses, and enforcing permissions on function calls. This facet is crucial for managing administrative privileges and controlling access to sensitive operations. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondProxy} from "@compose-protocol/diamond-proxy/DiamondProxy.sol"; -import {AccessControlFacet} from "@compose-protocol/diamond-proxy/facets/AccessControl/AccessControlFacet.sol"; - -diamond contract MyDiamond is DiamondProxy { - // Facet selectors - bytes4 private constant ACCESS_CONTROL_GRANT_ROLE_SELECTOR = AccessControlFacet.grantRole.selector; - bytes4 private constant ACCESS_CONTROL_HAS_ROLE_SELECTOR = AccessControlFacet.hasRole.selector; - - // Define roles - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - - function upgrade() external { - // ... deployment logic ... - } - - // Example of a function protected by a role - function mintTokens(address _to, uint256 _amount) external { - // Call the AccessControlFacet to check role - (bool success, ) = address(this).call(abi.encodeWithSelector(ACCESS_CONTROL_HAS_ROLE_SELECTOR, MINTER_ROLE, msg.sender)); - require(success, "Role check failed"); - - // ... minting logic ... - } - - // Example of granting a role - function grantAdminRole(address _account) external { - // Call the AccessControlFacet to grant role - (bool success, ) = address(this).call(abi.encodeWithSelector(ACCESS_CONTROL_GRANT_ROLE_SELECTOR, ADMIN_ROLE, _account)); - require(success, "Grant role failed"); - } -}`} - - -## Best Practices - - -- Initialize roles and assign initial admin privileges during diamond deployment. -- Use `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple role assignments. -- Define custom roles using `keccak256` for granular permission control. - - -## Security Considerations - - -Ensure that the initial deployment correctly assigns administrative roles to trusted accounts. The `setRoleAdmin` function must be carefully protected to prevent unauthorized changes to role hierarchies. All functions that modify roles or grant permissions should be callable only by the designated role administrators. - - -
- -
- - diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx deleted file mode 100644 index d5276a05..00000000 --- a/website/docs/library/access/AccessControl/AccessControlMod.mdx +++ /dev/null @@ -1,443 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Manage roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage roles and permissions within a diamond. - - - -- Role-based access control for granular permission management. -- Functions to grant, revoke, and check for role ownership (`grantRole`, `revokeRole`, `hasRole`). -- Ability to define and manage administrative roles for other roles (`setRoleAdmin`). - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControl module provides a robust framework for managing role-based access control within Compose diamonds. It enables fine-grained permission management, ensuring that only authorized accounts can execute specific functions. This is critical for maintaining the integrity and security of complex diamond applications. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControl} from "@compose/diamond-core/contracts/modules/accesscontrol/IAccessControl.sol"; - -contract MyFacet { - IAccessControl accessControl; - - constructor(address _diamondProxy) { - accessControl = IAccessControl(_diamondProxy); - } - - function grantAdminRole(address _account) external { - bytes32 adminRole = accessControl.getStorage().DEFAULT_ADMIN_ROLE; - accessControl.grantRole(adminRole, _account); - } - - function isOwner(address _account) external view returns (bool) { - bytes32 adminRole = accessControl.getStorage().DEFAULT_ADMIN_ROLE; - return accessControl.hasRole(adminRole, _account); - } -}`} - - -## Best Practices - - -- Use `requireRole` for enforcing access control checks directly within facet functions, reverting with `AccessControlUnauthorizedAccount` if unauthorized. -- Ensure roles and their admin roles are clearly defined and managed, ideally through dedicated administrative facets or initialization scripts. -- Treat role grants and revokes as sensitive operations, implementing appropriate access controls for managing these functions themselves. - - -## Integration Notes - - -This module relies on a dedicated storage slot within the diamond's storage. Facets interact with it via the `IAccessControl` interface. Changes to role assignments or admin roles are immediately reflected and visible to all facets through the diamond proxy. The `getStorage()` function provides direct access to the module's internal state, enabling facets to query role assignments and configurations. - - -
- -
- - diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json deleted file mode 100644 index 312754c7..00000000 --- a/website/docs/library/access/AccessControl/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Access Control", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/access/AccessControl", - "description": "Role-based access control (RBAC) pattern." - } -} diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx deleted file mode 100644 index 2a11981d..00000000 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,397 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Manage roles and pausing functionality for diamond access control." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage roles and pausing functionality for diamond access control. - - - -- Role-specific pausing: Allows individual roles to be paused independently. -- Admin-controlled pausing: Only the designated admin of a role can pause or unpause it. -- Integrated access control checks: `requireRoleNotPaused` enforces role activity. - - -## Overview - -This facet provides granular control over role-based access and allows for temporary pausing of specific roles. It integrates with the diamond's storage to manage role states and enforce pausing conditions, ensuring that only authorized actions can be performed when a role is active. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IComposeDiamond} from "@compose-protocol/diamond-contracts/contracts/interfaces/IComposeDiamond.sol"; -import {AccessControlPausableFacet} from "@compose-protocol/diamond-contracts/contracts/facets/AccessControlPausableFacet.sol"; - -contract Deployer { - address diamondAddress; - - function deploy() external { - // Assume diamondAddress is already set or deployed - diamondAddress = address(1); // Placeholder - - // Add AccessControlPausableFacet to the diamond - // Function selectors for AccessControlPausableFacet - bytes4[] memory selectors = new bytes4[](7); - selectors[0] = AccessControlPausableFacet.getAccessControlStorage.selector; - selectors[1] = AccessControlPausableFacet.getStorage.selector; - selectors[2] = AccessControlPausableFacet.isRolePaused.selector; - selectors[3] = AccessControlPausableFacet.pauseRole.selector; - selectors[4] = AccessControlPausableFacet.unpauseRole.selector; - selectors[5] = AccessControlPausableFacet.requireRoleNotPaused.selector; - // Add other selectors if needed, e.g., grantRole, revokeRole from AccessControl facet - - IComposeDiamond(diamondAddress).diamondCut( - new IComposeDiamond.FacetCut[]{ - (AccessControlPausableFacet.attach(address(0)), IComposeDiamond.FacetCutAction.Add, selectors) - }, - address(0), // target init contract - bytes("") // init data - ); - } - - function pauseMyRole() external { - address accessControlPausableFacetAddress = IComposeDiamond(diamondAddress).getFacetAddress(AccessControlPausableFacet.getStorage.selector); - bytes32 role = keccak256("MY_ROLE"); // Example role - AccessControlPausableFacet(accessControlPausableFacetAddress).pauseRole(role); - } - - function unpauseMyRole() external { - address accessControlPausableFacetAddress = IComposeDiamond(diamondAddress).getFacetAddress(AccessControlPausableFacet.getStorage.selector); - bytes32 role = keccak256("MY_ROLE"); // Example role - AccessControlPausableFacet(accessControlPausableFacetAddress).unpauseRole(role); - } - - function checkRoleStatus() external view returns (bool) { - address accessControlPausableFacetAddress = IComposeDiamond(diamondAddress).getFacetAddress(AccessControlPausableFacet.getStorage.selector); - bytes32 role = keccak256("MY_ROLE"); // Example role - return AccessControlPausableFacet(accessControlPausableFacetAddress).isRolePaused(role); - } -}`} - - -## Best Practices - - -- Ensure the `AccessControlFacet` is also deployed and configured for role management before using this facet for pausing. -- The `pauseRole` and `unpauseRole` functions require the caller to be the admin of the role, so proper role administration is crucial. -- Use `requireRoleNotPaused` within other facets to enforce role availability before executing sensitive operations. - - -## Security Considerations - - -Access control relies on the underlying AccessControl system correctly assigning role admins. Incorrect admin assignment could lead to unauthorized pausing or unpausing. The `requireRoleNotPaused` function prevents execution if a role is paused, mitigating risks associated with operations that should not occur during a pause. Ensure that the caller has the necessary permissions to call `pauseRole` and `unpauseRole` to prevent unauthorized state changes. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx deleted file mode 100644 index e0c25f8b..00000000 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx +++ /dev/null @@ -1,379 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Manages role-based access control with pausing capabilities." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages role-based access control with pausing capabilities. - - - -- Role-specific pausing: Allows individual roles to be paused independently of others. -- Integrated access control checks: Combines role membership verification with pause status. -- Reverts with specific errors: Provides `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` for clear error handling. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module integrates role-based access control with pausing functionality, allowing specific roles to be temporarily suspended. It ensures that operations protected by a role are only executable when that role is not paused, enhancing safety and control during upgrades or emergencies. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausableMod} from "@compose/modules/access-control-pausable/IAccessControlPausableMod.sol"; - -contract MyFacet { - IAccessControlPausableMod public immutable accessControlPausableMod; - - constructor(address _accessControlPausableMod) { - accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); - } - - uint256 public constant MY_ROLE = 1; - - function doSomethingProtected() external { - accessControlPausableMod.requireRoleNotPaused(MY_ROLE); - // ... protected logic here ... - } - - function pauseMyRole() external { - // Only an authorized entity can pause - accessControlPausableMod.pauseRole(MY_ROLE); - } - - function unpauseMyRole() external { - // Only an authorized entity can unpause - accessControlPausableMod.unpauseRole(MY_ROLE); - } -}`} - - -## Best Practices - - -- Implement `requireRoleNotPaused` at the entry point of functions requiring role protection to enforce access control and pause status. -- Use `pauseRole` and `unpauseRole` judiciously, typically managed by a separate administrative role or owner, to control operational availability. -- Ensure the `MY_ROLE` identifier used in the facet is consistent with the role identifier managed by the AccessControl module. - - -## Integration Notes - - -This module interacts with the diamond's storage, typically requiring the `AccessControlPausableMod` struct to be stored in a dedicated slot. Facets can access the module's functionality via its interface. The `requireRoleNotPaused` function checks both role membership (delegated to the AccessControl component) and the pause status managed by this module. Ensure that the AccessControl module is initialized and roles are defined before using this module's pausing features. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json deleted file mode 100644 index 351b5058..00000000 --- a/website/docs/library/access/AccessControlPausable/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Pausable Access Control", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/access/AccessControlPausable", - "description": "RBAC with pause functionality." - } -} diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx deleted file mode 100644 index 618bf8b0..00000000 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,461 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages time-bound role assignments within a diamond. - - - -- Time-bound role granting with explicit expiration timestamps. -- Automatic expiration enforcement via `isRoleExpired` and `requireValidRole`. -- Admin-only control for granting and revoking temporal roles. - - -## Overview - -This facet extends Compose's access control system by introducing time-bound roles. It allows administrators to grant roles with specific expiration timestamps and enforce these expirations, providing granular control over permissions over time. This is crucial for temporary access needs or managing roles that should automatically deactivate. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose-protocol/diamond-contracts/diamond/IDiamondCut.sol"; -import {AccessControlTemporalFacet} from "./facets/AccessControlTemporalFacet.sol"; - -contract DeployDiamond { - // ... other facet addresses ... - address accessControlTemporalFacetAddress; - - function deploy() public { - // ... deployment logic ... - - address[] memory facetAddresses = new address[](1); - facetAddresses[0] = accessControlTemporalFacetAddress; - - bytes32[] memory functionSelectors = new bytes32[](7); - // Placeholder for actual selectors - functionSelectors[0] = AccessControlTemporalFacet.getAccessControlStorage.selector; - functionSelectors[1] = AccessControlTemporalFacet.getStorage.selector; - functionSelectors[2] = AccessControlTemporalFacet.getRoleExpiry.selector; - functionSelectors[3] = AccessControlTemporalFacet.isRoleExpired.selector; - functionSelectors[4] = AccessControlTemporalFacet.grantRoleWithExpiry.selector; - functionSelectors[5] = AccessControlTemporalFacet.revokeTemporalRole.selector; - functionSelectors[6] = AccessControlTemporalFacet.requireValidRole.selector; - - // ... diamond cut data ... - - // Example role grant - uint64 expiryTimestamp = uint64(block.timestamp) + 3600; // Role expires in 1 hour - AccessControlTemporalFacet(accessControlTemporalFacetAddress).grantRoleWithExpiry(bytes32("ROLE_TEMPORARY"), msg.sender, expiryTimestamp); - - // Example role check - if (AccessControlTemporalFacet(accessControlTemporalFacetAddress).isRoleExpired(bytes32("ROLE_TEMPORARY"), msg.sender)) { - // Role has expired - } - } -}`} - - -## Best Practices - - -- Grant roles with expiry only when necessary for temporary access, ensuring the `admin` of the role is the caller. -- Regularly audit temporal role assignments to prevent unintended persistent access after intended expiry. -- Use `requireValidRole` within other facets to enforce time-bound access control checks before critical operations. - - -## Security Considerations - - -Access to grant and revoke temporal roles is restricted to the role's administrator, preventing unauthorized parties from manipulating time-bound permissions. The `requireValidRole` function prevents reentrancy by reverting if the role has expired. Ensure that the `admin` role itself is properly secured to prevent unauthorized temporal role management. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx deleted file mode 100644 index 610fc473..00000000 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,477 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Manages time-bound role assignments for access control." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages time-bound role assignments for access control. - - - -- Grants roles with a specified expiry timestamp, automating revocation. -- Provides a `requireValidRole` function to enforce non-expired role checks. -- Offers functions to query role expiry status and revoke temporal roles. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module introduces temporal access control, allowing roles to be granted with specific expiry timestamps. It ensures that access is automatically revoked once the validity period ends, enhancing security and manageability for diamond applications. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; - -contract MyDiamondFacet { - IAccessControlTemporalMod public immutable accessControlTemporalMod; - - constructor(address _accessControlTemporalMod) { - accessControlTemporalMod = IAccessControlTemporalMod(_accessControlTemporalMod); - } - - function grantAdminRoleWithExpiry(address _user, uint64 _expiry) external { - // Assuming 'DEFAULT_ADMIN_ROLE' is a constant defined elsewhere - bytes32 role = DEFAULT_ADMIN_ROLE; - accessControlTemporalMod.grantRoleWithExpiry(role, _user, _expiry); - } - - function checkAdminAccess(address _user) external view { - bytes32 role = DEFAULT_ADMIN_ROLE; - accessControlTemporalMod.requireValidRole(role, _user); - // Access is permitted - } -}`} - - -## Best Practices - - -- Utilize `grantRoleWithExpiry` to assign roles with clear expiration dates, reducing the need for manual revocation. -- Implement checks using `requireValidRole` before critical operations to ensure active and unexpired role assignments. -- Ensure the `AccessControlTemporalMod` facet is deployed and its address is correctly referenced by facets requiring temporal access control. - - -## Integration Notes - - -This module manages its state within its own storage slots, separate from the core Access Control storage. Facets interacting with this module should call its functions directly. The `requireValidRole` function will revert with `AccessControlRoleExpired` if the role's timestamp has passed, and `AccessControlUnauthorizedAccount` if the role is not assigned at all. Ensure the module is initialized and accessible via its facet address. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json deleted file mode 100644 index 3d0a61d6..00000000 --- a/website/docs/library/access/AccessControlTemporal/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Temporal Access Control", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/access/AccessControlTemporal", - "description": "Time-limited role-based access control." - } -} diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx deleted file mode 100644 index 39dd27a4..00000000 --- a/website/docs/library/access/Owner/OwnerFacet.mdx +++ /dev/null @@ -1,211 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "Manages contract ownership and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership and transfers. - - - -- Manages contract ownership. -- Supports transferring ownership to a new address. -- Allows for renouncing ownership. - - -## Overview - -The OwnerFacet provides essential ownership management capabilities for Compose diamonds. It allows the current owner to transfer ownership to a new address or renounce ownership entirely, ensuring clear control and accountability for administrative actions within the diamond. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose/facets/owner/IOwnerFacet.sol"; - -contract ExampleOwnerUsage { - IOwnerFacet private immutable _ownerFacet; - - constructor(address ownerFacetAddress) { - _ownerFacet = IOwnerFacet(ownerFacetAddress); - } - - function getCurrentOwner() external view returns (address) { - return _ownerFacet.owner(); - } - - function transferControl(address _newOwner) external { - _ownerFacet.transferOwnership(_newOwner); - } - - function giveUpOwnership() external { - _ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize the diamond with the owner address using the `transferOwnership` function during deployment. -- Ensure only the current owner can call `transferOwnership` and `renounceOwnership`. -- Handle ownership transfer carefully; consider using a multisig for critical contracts. - - -## Security Considerations - - -Access control is critical. Only the current owner should be able to execute ownership-related functions. Setting the new owner to `address(0)` effectively renounces ownership, making the contract effectively immutable regarding ownership changes unless re-initialized by a separate mechanism. Ensure the caller of `transferOwnership` is indeed the current owner. - - -
- -
- - diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx deleted file mode 100644 index ae23e7d3..00000000 --- a/website/docs/library/access/Owner/OwnerMod.mdx +++ /dev/null @@ -1,258 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "Manages contract ownership according to ERC-173." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership according to ERC-173. - - - -- Implements ERC-173 contract ownership. -- Provides `owner()`, `transferOwnership()`, and `requireOwner()` functions. -- Supports ownership renouncement by transferring to `address(0)`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod provides a standardized way to manage contract ownership, adhering to the ERC-173 standard. It enables secure ownership transfers and provides a mechanism for owner-only access control, crucial for administrative functions within a diamond. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; - -contract MyFacet { - uint256 constant OWNER_STORAGE_SLOT = 1; // Example slot - - struct OwnerStorage { - address owner; - // other storage variables... - } - - function _getOwnerStorage() internal pure returns (OwnerStorage storage) { - assembly { - storage.slot := OWNER_STORAGE_SLOT - } - } - - function owner() public view returns (address) { - return _getOwnerStorage().owner; - } - - function transferOwnership(address _newOwner) external { - IOwnerMod(_getOwnerStorage()).transferOwnership(_newOwner); - } - - function requireOwner() external view { - IOwnerMod(_getOwnerStorage()).requireOwner(); - } -}`} - - -## Best Practices - - -- Use `transferOwnership` to change the contract owner. Setting the new owner to `address(0)` renounces ownership. -- Employ `requireOwner` to restrict access to sensitive administrative functions to the current owner. -- Ensure the `OwnerMod` storage slot is correctly defined and not duplicated by other facets. - - -## Integration Notes - - -The OwnerMod utilizes a dedicated storage slot to store the owner's address. Facets can access this storage via the `getStorage` function or by directly referencing the defined storage slot. Any facet can read the owner address, but only the current owner can execute administrative functions protected by `requireOwner` or initiate ownership transfers. - - -
- -
- - diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json deleted file mode 100644 index f24d6058..00000000 --- a/website/docs/library/access/Owner/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Owner", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/access/Owner", - "description": "Single-owner access control pattern." - } -} diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx deleted file mode 100644 index da08a336..00000000 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,291 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership with a two-step transfer process. - - - -- Two-step ownership transfer for enhanced security. -- `owner()`, `pendingOwner()` view functions for state inspection. -- `renounceOwnership()` function to relinquish ownership. - - -## Overview - -The OwnerTwoStepsFacet provides a robust ownership management system for Compose diamonds. It enforces a two-step ownership transfer mechanism, requiring both the current owner to initiate a transfer and the new owner to accept it, enhancing security and preventing accidental ownership changes. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "@compose/contracts/facets/ownership/interfaces/IOwnerTwoStepsFacet.sol"; -import {DiamondProxy} from "@compose/contracts/core/DiamondProxy.sol"; - -contract OwnerUser { - IOwnerTwoStepsFacet ownerFacet; - - constructor(address diamondProxyAddress) { - ownerFacet = IOwnerTwoStepsFacet(diamondProxyAddress); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function initiateOwnershipTransfer(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function acceptNewOwnership() external { - ownerFacet.acceptOwnership(); - } - - function renounceCurrentOwnership() external { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize ownership transfers using `transferOwnership` and require the new owner to call `acceptOwnership` to complete the process. -- Only the current owner can initiate transfers or renounce ownership. -- The pending owner address is cleared after `acceptOwnership` or if the owner renounces. - - -## Security Considerations - - -Ensure that only authorized accounts can call `transferOwnership`, `acceptOwnership`, and `renounceOwnership`. The `OwnerUnauthorizedAccount` error is emitted if the caller is not the owner or pending owner as appropriate. Direct access to storage slots via `getOwnerStorage` and `getPendingOwnerStorage` should be used with caution and only by trusted facets. - - -
- -
- - diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx deleted file mode 100644 index 8d3388a3..00000000 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,309 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "Manages ERC-173 two-step contract ownership." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-173 two-step contract ownership. - - - -- Implements a secure ERC-173 compliant two-step ownership transfer. -- Provides `owner()` and `pendingOwner()` view functions for state inspection. -- Includes a `requireOwner()` internal modifier for access control within facets. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module implements a secure, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are intentional by requiring explicit acceptance from the new owner, preventing accidental or malicious transfers. This pattern is crucial for maintaining control and upgradeability in a decentralized environment. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoSteps} from "../interfaces/IOwnerTwoSteps.sol"; - -contract MyOwnerFacet { - // Assuming OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are accessible - // and the diamond storage layout is correctly set up. - IOwnerTwoSteps private ownerTwoStepsFacet; - - function initialize(address _diamondAddress) public { - // Assuming IOwnerTwoSteps interface is registered with the diamond proxy - ownerTwoStepsFacet = IOwnerTwoSteps(_diamondAddress); - } - - function transferContractOwnership(address _newOwner) external { - // Call the transferOwnership function from the OwnerTwoSteps module - ownerTwoStepsFacet.transferOwnership(_newOwner); - } - - function acceptContractOwnership() external { - // Call the acceptOwnership function from the OwnerTwoSteps module - ownerTwoStepsFacet.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return ownerTwoStepsFacet.owner(); - } - - function getPendingOwner() external view returns (address) { - return ownerTwoStepsFacet.pendingOwner(); - } - - function renounceContractOwnership() external { - ownerTwoStepsFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Use `transferOwnership` to initiate transfers and require the new owner to call `acceptOwnership` to finalize. -- Implement `requireOwner` checks within your facet functions to restrict sensitive operations to the current owner. -- Be aware that `renounceOwnership` permanently removes owner privileges; use with extreme caution. - - -## Integration Notes - - -This module manages ownership state within its own designated storage slots. Facets interacting with ownership should use the provided `IOwnerTwoSteps` interface to call functions like `transferOwnership` and `acceptOwnership`. The `owner` and `pendingOwner` states are globally accessible through the diamond proxy via the `IOwnerTwoSteps` interface. Ensure that the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are correctly defined and not conflicting with other facets' storage. - - -
- -
- - diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json deleted file mode 100644 index 54acbd6c..00000000 --- a/website/docs/library/access/OwnerTwoSteps/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Two-Step Owner", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/access/OwnerTwoSteps", - "description": "Two-step ownership transfer pattern." - } -} diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json deleted file mode 100644 index 32cd8855..00000000 --- a/website/docs/library/access/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Access Control", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/access", - "description": "Access control patterns for permission management in Compose diamonds." - } -} diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx deleted file mode 100644 index 90187263..00000000 --- a/website/docs/library/diamond/DiamondCutFacet.mdx +++ /dev/null @@ -1,422 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Manage diamond facets and functions programmatically." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and functions programmatically. - - - -- Supports adding, replacing, and removing functions and entire facets. -- Allows for optional execution of an initialization function during a cut operation. -- Provides granular control over the diamond's functional surface area. - - -## Overview - -The DiamondCutFacet provides the essential on-chain mechanism for upgrading and managing the functional surface area of a Compose diamond. It allows for the addition, replacement, and removal of functions across various facets, ensuring the diamond's capabilities can evolve over time. This facet is crucial for maintaining and extending the diamond's functionality post-deployment. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/contracts/facets/DiamondCut/IDiamondCut.sol"; - -contract Deployer { - // Assume diamondAddress is the address of your deployed diamond proxy - address diamondAddress; - - function upgradeDiamond() external { - // Get the DiamondCutFacet interface - IDiamondCut diamondCutFacet = IDiamondCut(diamondAddress); - - // Define facet cut data - // Example: Add a new ERC721 facet - address newErc721FacetAddress = address(0x123...); // Address of the deployed ERC721 facet contract - bytes4[] memory erc721Selectors = new bytes4[](2); - erc721Selectors[0] = IDiamondCut.getOwnerStorage.selector; // Example selector - erc721Selectors[1] = IDiamondCut.getDiamondStorage.selector; // Example selector - - // Execute the diamond cut - // Note: The owner must have permissions to call diamondCut - diamondCutFacet.diamondCut( - new IDiamondCut.FacetCut[](0), // No facets to remove - new IDiamondCut.FacetCut[](1){ \ - facetAddress: newErc721FacetAddress, - action: IDiamondCut.FacetCutAction.ADD, - selectors: erc721Selectors - }, - address(0), // No init function to call - bytes("") // No init data - ); - } -}`} - - -## Best Practices - - -- Ensure the caller has the necessary permissions (e.g., owner role) before invoking `diamondCut`. -- Carefully manage facet addresses and selector mappings to prevent unintended function overwrites or removals. -- Store facet deployment addresses off-chain or in a trusted registry for secure upgrades. - - -## Security Considerations - - -The `diamondCut` function is highly sensitive and should only be callable by authorized addresses. Incorrect usage can lead to loss of functionality or unintended state changes. Ensure all function selectors are correctly mapped to their corresponding facet addresses. Be cautious when replacing existing functions, especially immutable ones, as this can break existing integrations. Initialization functions executed during `diamondCut` must be carefully audited for reentrancy and other vulnerabilities. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx deleted file mode 100644 index eae33e26..00000000 --- a/website/docs/library/diamond/DiamondCutMod.mdx +++ /dev/null @@ -1,396 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Manages diamond facet additions, removals, and replacements." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages diamond facet additions, removals, and replacements. - - - -- Dynamically add, remove, or replace functions on the diamond proxy. -- Supports batch operations for multiple facet changes in a single transaction. -- Includes error handling for common issues like non-existent selectors or attempting to modify immutable functions. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCutMod provides essential functions for dynamically managing the facets attached to a Compose diamond. It allows for the addition of new functions, the removal of existing ones, and the replacement of functions with new implementations, all while ensuring the integrity and safety of the diamond's logic. This module is crucial for upgrading and evolving diamond functionality post-deployment. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/contracts/diamond/interfaces/IDiamondCut.sol"; -import {DiamondCutMod} from "@compose/contracts/diamond/modules/DiamondCutMod.sol"; - -contract MyDiamondFacet { - // Assume IDiamondCut is already implemented on the diamond - IDiamondCut internal diamondCut = IDiamondCut(address(this)); - - function upgradeMyFacet(address _newFacetAddress, bytes4[] memory _selectors) external { - // Example: Replacing functions - // Ensure _newFacetAddress is a valid facet contract - // Ensure _selectors are the functions to be replaced from the old facet - // and are present in the new facet. - diamondCut.diamondCut( - new IDiamondCut.FacetCut[]({ - IDiamondCut.FacetCut({ - facetAddress: _newFacetAddress, - action: IDiamondCut.FacetCutAction.Replace, - selectors: _selectors - }) - }), - address(0), // No init function - \"\" // No init data - ); - } - - function addNewFunctionality(address _newFacetAddress, bytes4[] memory _selectors) external { - diamondCut.diamondCut( - new IDiamondCut.FacetCut[]({ - IDiamondCut.FacetCut({ - facetAddress: _newFacetAddress, - action: IDiamondCut.FacetCutAction.Add, - selectors: _selectors - }) - }), - address(0), - \"\" - ); - } -}`} - - -## Best Practices - - -- Use `diamondCut` with `FacetCutAction.Replace` carefully, ensuring the new facet's selectors are compatible with the existing diamond logic to avoid breaking functionality. -- Always provide valid `selectors` when adding or replacing functions. An empty `selectors` array for `Add` actions will revert with `NoSelectorsProvidedForFacet`. -- Be aware of `Immutable` functions. Attempting to remove or replace them will revert with specific errors, preventing accidental modification of core diamond logic. - - -## Integration Notes - - -The `DiamondCutMod` interacts with the diamond's storage to manage the mapping of selectors to facet addresses. When functions are added, removed, or replaced, these changes are immediately reflected in the diamond's routing logic. Facets that interact with the diamond proxy should be aware that the underlying facet implementations can change. The `diamondCut` function can optionally execute an initialization function via `delegatecall` after the cut operation, allowing for state setup in new facets. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx deleted file mode 100644 index 27293508..00000000 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,254 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "Query diamond facets, addresses, and function selectors." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Query diamond facets, addresses, and function selectors. - - - -- Provides a standardized interface for querying diamond components. -- Optimized for gas efficiency when querying large diamonds with many facets and selectors. -- Supports querying individual facet addresses, all facet addresses, and selectors per facet. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query which facets are registered, their associated addresses, and the function selectors they implement. This is crucial for understanding the diamond's structure, debugging, and building compatible extensions. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondLoupe} from "@compose/diamond/facets/DiamondLoupe/IDiamondLoupe.sol"; - -contract DiamondConsumer { - IDiamondLoupe public diamondLoupeFacet; - - constructor(address _diamondAddress) { - diamondLoupeFacet = IDiamondLoupe(_diamondAddress); - } - - function getFacetAddresses() external view returns (address[] memory) { - return diamondLoupeFacet.facetAddresses(); - } - - function getFacetSelectors(address _facetAddress) external view returns (bytes4[] memory) { - return diamondLoupeFacet.facetFunctionSelectors(_facetAddress); - } - - function getAllFacets() external view returns (IDiamondLoupe.Facet[] memory) { - return diamondLoupeFacet.facets(); - } -}`} - - -## Best Practices - - -- Initialize the facet with the diamond's address to enable introspection. -- Use the returned data to verify diamond state or to dynamically route calls. -- Cache facet addresses and selectors locally if frequent querying is required to minimize on-chain calls. - - -## Security Considerations - - -This facet is primarily for read operations and does not directly manage state changes. Ensure that the diamond address provided during initialization is the correct one to prevent querying unintended contracts. The gas cost of extensive querying should be considered in gas-sensitive applications. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx deleted file mode 100644 index 17045aa2..00000000 --- a/website/docs/library/diamond/DiamondMod.mdx +++ /dev/null @@ -1,237 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Manage diamond facets and internal storage." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and internal storage. - - - -- Enables programmatic addition of facets and their function selectors during diamond deployment. -- Provides a secure mechanism (`getStorage`) to inspect the diamond's internal storage layout. -- Acts as the central point for function dispatch via `diamondFallback`, routing calls to the appropriate facet. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides essential internal functions for managing facets within a diamond proxy, including adding new facets and providing access to diamond storage. It is crucial for the diamond's initialization and runtime operation, ensuring facets are correctly registered and accessible. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondMod} from "@compose/contracts/diamond/IDiamondMod.sol"; - -contract MyFacet { - IDiamondMod internal diamondMod; - - constructor(address _diamondMod) { - diamondMod = IDiamondMod(_diamondMod); - } - - function addMyFacet() external { - // Example: Illustrative, actual facet registration is done at deployment. - // This function demonstrates calling an internal diamond function for context. - // diamondMod.addFacets(...); // This is typically called by the diamond deployer. - } - - function getDiamondStorage() external view returns (bytes memory) { - return diamondMod.getStorage(); - } -}`} - - -## Best Practices - - -- Facet addition is restricted to the diamond deployment phase to maintain integrity and predictability. -- Utilize `getStorage()` to safely access and inspect internal diamond storage state, ensuring no direct manipulation that could break invariants. -- Understand that `diamondFallback` is the core dispatch mechanism; ensure all facet functions are correctly registered to be discoverable. - - -## Integration Notes - - -The `DiamondMod` contract manages the diamond's core state, including the mapping of function selectors to facet addresses and the internal storage layout. Facets interact with `DiamondMod` primarily through the `diamondFallback` mechanism for function execution and `getStorage` for introspection. Changes to facet registrations via `addFacets` are typically performed only during the initial diamond deployment. - - -
- -
- - diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json deleted file mode 100644 index 423c02c9..00000000 --- a/website/docs/library/diamond/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Diamond Core", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/diamond", - "description": "Core diamond proxy functionality for ERC-2535 diamonds." - } -} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx deleted file mode 100644 index 0cab7692..00000000 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ /dev/null @@ -1,129 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Example Diamond contract for Compose framework" -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Example Diamond contract for Compose framework - - - -- Initializes diamond with facets and owner. -- Registers function selectors for delegatecall routing. -- Provides a basic structural example for Compose diamonds. - - -## Overview - -This contract serves as a foundational example for a Compose diamond. It demonstrates diamond initialization by registering facets and their function selectors, enabling delegatecall routing. It establishes ownership and sets up the initial diamond structure. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose-diamond/diamond-cut/src/IDiamondCut.sol"; -import {ExampleDiamond} from "./ExampleDiamond.sol"; - -contract DeployExampleDiamond { - address public diamondAddress; - - function deploy() public { - // Define facets to be added - ExampleDiamond.FacetCut[] memory facets = new ExampleDiamond.FacetCut[](1); - bytes4[] memory selectors = new bytes4[](1); - selectors[0] = ExampleDiamond.deploy.selector; // Assuming a function named 'deploy' exists in a facet - facets[0] = ExampleDiamond.FacetCut( - address(1), // Replace with actual facet address - ExampleDiamond.FacetCutAction.Add, - selectors - ); - - // Deploy the diamond and initialize it - diamondAddress = address(new ExampleDiamond(facets, msg.sender)); - } -}`} - - -## Best Practices - - -- Use explicit initializer functions for setting up diamond contracts and their facets. -- Ensure all facets are registered with their correct function selectors during deployment. -- Manage ownership and access control carefully, especially during initialization. - - -## Security Considerations - - -The `constructor` function is critical for setting up the diamond's initial state. Ensure facet addresses and selectors are accurate to prevent routing to unintended functions. Ownership is set in the constructor, so the `msg.sender` should be a trusted deployer. - - -
- -
- - diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json deleted file mode 100644 index d6c0dc0d..00000000 --- a/website/docs/library/diamond/example/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "example", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/diamond/example", - "description": "example components for Compose diamonds." - } -} diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx deleted file mode 100644 index 3f585714..00000000 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx +++ /dev/null @@ -1,157 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "Implements ERC-165 interface detection for diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements ERC-165 interface detection for diamonds. - - - -- Implements the ERC-165 standard for interface detection. -- Provides internal functions for registering and querying supported interfaces. -- Designed for seamless integration with the Compose diamond storage pattern. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod provides the necessary storage and internal functions to comply with the ERC-165 standard for interface detection. This allows diamonds and their facets to programmatically declare which interfaces they support, enhancing interoperability and discoverability within the Compose ecosystem. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC165, IERC165Mod} from "@compose/modules/erc165/LibERC165.sol"; -import {IDiamondCut} from "@compose/diamond/IDiamond.sol"; - -contract MyERC721Facet { - /** - * @notice Initializes the facet, registering ERC721 and ERC165 interfaces. - * @param _diamondCut Address of the DiamondCut facet for initialization. - */ - function initialize(IDiamondCut _diamondCut) external { - // ... other initialization logic ... - - // Register ERC721 and ERC165 support - LibERC165.registerInterface(type(IERC721).interfaceId); - LibERC165.registerInterface(type(IERC165).interfaceId); - } - - // ... other facet functions ... -}`} - - -## Best Practices - - -- Register supported interfaces during facet initialization using `LibERC165.registerInterface()`. -- Ensure the ERC165Mod is added to the diamond, typically as part of the diamond's base facets. -- Call `LibERC165.supportsInterface()` from facets or external contracts to check for interface support. - - -## Integration Notes - - -The ERC165Mod utilizes a dedicated storage slot to maintain a mapping of supported interface IDs. Facets can access this storage indirectly via the library functions. When adding the ERC165Mod as a facet, ensure its initialization function is called to register the interfaces supported by the diamond and its facets. The `supportsInterface` function is available externally via the diamond proxy to query interface support. - - -
- -
- - diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json deleted file mode 100644 index 2ed43f06..00000000 --- a/website/docs/library/interfaceDetection/ERC165/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-165", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/interfaceDetection/ERC165", - "description": "ERC-165 components for Compose diamonds." - } -} diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json deleted file mode 100644 index 2126981f..00000000 --- a/website/docs/library/interfaceDetection/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Interface Detection", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/interfaceDetection", - "description": "ERC-165 interface detection support." - } -} diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx deleted file mode 100644 index 5c52db22..00000000 --- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx +++ /dev/null @@ -1,678 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "Manages ERC-1155 fungible and non-fungible tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 fungible and non-fungible tokens. - - - -- Supports both fungible and non-fungible tokens within a single facet. -- Implements batched transfer and balance checking functions for efficiency. -- Provides flexible URI management for token metadata. - - -## Overview - -The ERC1155Facet provides a comprehensive implementation for the ERC-1155 Multi-Token Standard within a Compose diamond. It handles token balances, approvals, and transfers for multiple token types, enabling both fungible and non-fungible assets to coexist and be managed efficiently through the diamond proxy. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Facet} from "@compose/contracts/facets/ERC1155/IERC1155Facet.sol"; -import {ERC1155FacetSelectors} from "@compose/contracts/facets/ERC1155/ERC1155FacetSelectors.sol"; - -contract MyDiamond is IDiamondCut { - // ... deployment logic ... - - function _diamondCut() internal override returns (FacetCut[] memory) { - // ... other facet cuts ... - return - abi.encodePacked( - FacetCut({ - facet: address(new ERC1155Facet()), - action: IDiamondCut.FacetCutAction.ADD, - selectors: ERC1155FacetSelectors.ALL - }) - ); - } - - function getERC1155Facet() public view returns (IERC1155Facet) { - return IERC1155Facet(address(this)); - } -} - -contract Consumer { - function getBalance(address diamond, address account, uint256 id) public view returns (uint256) { - return diamond.balanceOf(account, id); - } - - function getTokenURI(address diamond, uint256 id) public view returns (string memory) { - return diamond.uri(id); - } -}`} - - -## Best Practices - - -- Initialize the ERC1155Facet with appropriate base URI and token URIs during diamond deployment. -- Utilize `safeTransferFrom` and `safeBatchTransferFrom` for all token transfers to ensure adherence to the ERC-1155 standard. -- Manage approvals carefully using `setApprovalForAll` to control operator permissions. - - -## Security Considerations - - -Ensure that `safeTransferFrom` and `safeBatchTransferFrom` are used exclusively to prevent reentrancy issues and guarantee proper handling of token transfers. Verify that the caller has sufficient balance and necessary approvals before initiating transfers. Input validation for token IDs and amounts is crucial to prevent unexpected behavior or denial of service. - - -
- -
- - diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx deleted file mode 100644 index f299b351..00000000 --- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx +++ /dev/null @@ -1,605 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "Manages ERC-1155 token minting, burning, and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 token minting, burning, and transfers. - - - -- Supports both single and batch minting and burning of ERC-1155 tokens. -- Implements safe transfer logic for single and batch operations, including ERC1155Receiver validation. -- Manages token URIs with `setBaseURI` and `setTokenURI` functions, emitting `URI` events. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC1155Mod provides core ERC-1155 token functionalities, including minting, burning, and safe transfers. It integrates seamlessly with the diamond storage pattern, allowing facets to manage token balances and metadata efficiently. This module ensures compliance with ERC-1155 standards for both single and batch operations. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Mod} from "@compose/modules/erc1155/IERC1155Mod.sol"; - -contract MyERC1155Facet { - // Assume diamond storage is accessible and ERC1155Mod is initialized - IERC1155Mod internal constant ERC1155Mod = IERC1155Mod(address(this)); - - function mintNewTokens(address _to, uint256 _id, uint256 _amount) external { - // Mint tokens to the recipient - ERC1155Mod.mint(_to, _id, _amount); - } - - function burnExistingTokens(address _from, uint256 _id, uint256 _amount) external { - // Burn tokens from the sender - ERC1155Mod.burn(_from, _id, _amount); - } - - function transferSomeTokens(address _from, address _to, uint256 _id, uint256 _amount) external { - // Safely transfer tokens - ERC1155Mod.safeTransferFrom(_from, _to, _id, _amount, ""); - } -}`} - - -## Best Practices - - -- Ensure the ERC1155Mod is correctly initialized within the diamond's storage layout. -- Always validate `_to` and `_from` addresses before performing transfers or mints to prevent unexpected behavior. -- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, and `ERC1155MissingApprovalForAll` errors appropriately in your facet logic. - - -## Integration Notes - - -The ERC1155Mod operates on a dedicated storage slot within the diamond's global storage. Facets interacting with this module should use the `IERC1155Mod` interface to call its functions. Token balances and URI data are stored and managed by this module, and changes are immediately visible to all facets that have access to the diamond's storage. The `getStorage` function provides direct access to the module's internal storage struct. - - -
- -
- - diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json deleted file mode 100644 index 220e1ab9..00000000 --- a/website/docs/library/token/ERC1155/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-1155", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC1155", - "description": "ERC-1155 multi-token implementations." - } -} diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx deleted file mode 100644 index c2c6c201..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ /dev/null @@ -1,256 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "Burn ERC-20 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC-20 tokens within a Compose diamond. - - - -- Allows burning of ERC-20 tokens directly from user balances. -- Supports burning tokens from other accounts via allowances. -- Emits `Transfer` events to the zero address upon successful burning, adhering to ERC-20 standards. - - -## Overview - -The ERC20BurnFacet provides functionality to burn ERC-20 tokens directly within a Compose diamond. It enables users to reduce token supply by destroying their own tokens or tokens they have an allowance for, ensuring compliance with ERC-20 standards. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; - -contract ERC20BurnConsumer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnMyTokens(uint256 _amount) external { - // Get the ERC20BurnFacet interface - IERC20BurnFacet burnFacet = IERC20BurnFacet(diamondAddress); - - // Burn tokens from the caller's balance - burnFacet.burn(_amount); - } - - function burnTokensFromSomeone(address _from, uint256 _amount) external { - // Get the ERC20BurnFacet interface - IERC20BurnFacet burnFacet = IERC20BurnFacet(diamondAddress); - - // Burn tokens from another account using allowance - burnFacet.burnFrom(_from, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC20BurnFacet is properly registered and accessible via the diamond proxy. -- Use `burnFrom` only after approving the necessary allowance to the diamond address. -- Handle `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` errors appropriately. - - -## Security Considerations - - -This facet relies on the underlying ERC-20 token contract for balance and allowance checks. Ensure the diamond's access control mechanisms are correctly configured to prevent unauthorized burning. The `burnFrom` function requires prior approval of an allowance, which should be managed carefully by token holders. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx deleted file mode 100644 index 6c3e1364..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx +++ /dev/null @@ -1,564 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "Standard ERC-20 token functionality for Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Standard ERC-20 token functionality for Compose diamonds. - - - -- Implements all standard ERC-20 functions (name, symbol, decimals, totalSupply, balanceOf, allowance, approve, transfer, transferFrom). -- Leverages the Compose Diamond storage pattern for state management. -- Emits standard ERC-20 `Transfer` and `Approval` events. - - -## Overview - -The ERC20Facet provides a complete implementation of the ERC-20 token standard, enabling fungible token operations within a Compose diamond. It exposes standard functions for querying token details, managing balances, and handling allowances and transfers, making it a fundamental building block for tokenized assets. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Facet} from "@compose-protocol/diamond-contracts/facets/ERC20/IERC20Facet.sol"; - -contract ERC20Consumer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getTokenName() external view returns (string memory) { - IERC20Facet erc20Facet = IERC20Facet(diamondAddress); - return erc20Facet.name(); - } - - function transferTokens(address _to, uint256 _amount) external { - IERC20Facet erc20Facet = IERC20Facet(diamondAddress); - erc20Facet.transfer(_to, _amount); - } -}`} - - -## Best Practices - - -- Initialize the ERC20 storage struct correctly during diamond deployment or upgrade. -- Ensure proper access control is implemented at the diamond level for administrative functions if applicable (though standard ERC-20 functions are typically permissionless). -- Use `approve` before `transferFrom` to manage token spending permissions securely. - - -## Security Considerations - - -Standard ERC-20 security considerations apply. Ensure input validation for addresses and amounts. Be mindful of potential reentrancy if custom logic interacts with token transfers. The `approve` function can lead to unintended spending if not used carefully by users; consider implementing checks for zero allowance before approving if your application logic requires it. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx deleted file mode 100644 index 8e12b534..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx +++ /dev/null @@ -1,425 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "ERC-20 token logic with core transfer, mint, and burn operations." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token logic with core transfer, mint, and burn operations. - - - -- Supports standard ERC-20 `transfer` and `transferFrom` operations. -- Implements `mint` for creating new tokens and `burn` for destroying tokens. -- Manages token `approvals` for delegated transfers. -- Provides a `getStorage` function for direct access to internal storage, enabling interoperability. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod module provides essential ERC-20 token functionality, including transfers, minting, and burning. It manages token balances and allowances, adhering to the ERC-20 standard. Integrating this module allows diamonds to support fungible tokens with standard on-chain operations. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod } from "@compose/modules/erc20/IERC20Mod.sol"; -import { ERC20Mod } from "@compose/modules/erc20/ERC20Mod.sol"; - -contract MyDiamondFacet { - ERC20Mod internal erc20; - - // Assumes ERC20Mod is initialized and its storage slot is known - constructor(address _erc20StorageAddress) { - erc20 = ERC20Mod(_erc20StorageAddress); - } - - function transferTokens(address _to, uint256 _amount) external { - erc20.transfer(msg.sender, _to, _amount); - } - - function approveSpender(address _spender, uint256 _amount) external { - erc20.approve(msg.sender, _spender, _amount); - } - - function burnTokens(uint256 _amount) external { - erc20.burn(msg.sender, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC20Mod facet is correctly initialized with the appropriate storage slot. -- Handle `ERC20InsufficientBalance`, `ERC20InsufficientAllowance`, `ERC20InvalidReceiver`, `ERC20InvalidSender`, and `ERC20InvalidSpender` errors to manage token operations gracefully. -- Be mindful of total supply changes when minting or burning tokens. - - -## Integration Notes - - -The ERC20Mod relies on a specific storage slot to maintain its state, including token balances, allowances, and total supply. Facets interacting with this module must either call its functions through the diamond proxy or directly access its storage via the `getStorage` function if they are integrated within the same diamond and understand the storage layout. Ensure that no other facets or modules overwrite the storage slot allocated for ERC20Mod, as this would lead to data corruption. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json deleted file mode 100644 index c72db04e..00000000 --- a/website/docs/library/token/ERC20/ERC20/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC20/ERC20", - "description": "ERC-20 fungible token implementations." - } -} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx deleted file mode 100644 index a1cb3995..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,417 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain ERC20 token bridging operations." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Facilitates cross-chain ERC20 token bridging operations. - - - -- Cross-chain minting and burning capabilities for ERC20 tokens. -- Role-based access control restricted to `trusted-bridge` addresses. -- Internal functions for retrieving storage and validating bridge authenticity. - - -## Overview - -The ERC20BridgeableFacet enables secure and controlled cross-chain transfers of ERC20 tokens. It provides functions for minting and burning tokens on behalf of trusted bridge operators, ensuring integrity and adherence to access control policies. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableFacet} from "@compose/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; - -contract Deployer { - address diamondAddress; - - function deploy() public { - // ... diamond deployment logic ... - diamondAddress = address(0xYourDiamondProxyAddress); - } - - function mintCrosschain(address _to, uint256 _amount) public { - IERC20BridgeableFacet bridgeFacet = IERC20BridgeableFacet(diamondAddress); - // Assuming caller has the 'trusted-bridge' role - bridgeFacet.crosschainMint(_to, _amount); - } - - function burnCrosschain(address _from, uint256 _amount) public { - IERC20BridgeableFacet bridgeFacet = IERC20BridgeableFacet(diamondAddress); - // Assuming caller has the 'trusted-bridge' role - bridgeFacet.crosschainBurn(_from, _amount); - } -}`} - - -## Best Practices - - -- Ensure only trusted addresses are granted the `trusted-bridge` role for cross-chain operations. -- Utilize `checkTokenBridge` internally or externally to verify bridge authorization before critical actions. -- Store ERC20 and access control configurations in designated storage slots for organized state management. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are permissioned and callable only by addresses holding the `trusted-bridge` role. The `checkTokenBridge` function enforces this role check. Ensure the `trusted-bridge` role is managed securely to prevent unauthorized token minting or burning. Reentrancy is not a concern as these functions do not make external calls. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx deleted file mode 100644 index 65d50973..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,431 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "Manage cross-chain ERC20 token transfers and burns." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage cross-chain ERC20 token transfers and burns. - - - -- Cross-chain minting and burning of ERC20 tokens. -- Strict access control for bridge operations via the `trusted-bridge` role. -- Utilizes internal assembly for efficient storage access. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module enables secure cross-chain operations for ERC20 tokens. It allows trusted bridge addresses to mint or burn tokens on behalf of users, facilitating interoperability between different blockchain networks. Access is strictly controlled via an access control role. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableMod} from "@compose/contracts/src/modules/ERC20BridgeableMod.sol"; -import {IDiamondStorage} from "@compose/contracts/src/interfaces/IDiamondStorage.sol"; - -contract ERC20BridgeableFacet { - address immutable DIAMOND_STORAGE_SLOT = address(uint160(uint256(keccak256("diamond.storage.compose.v1")))); - - function _getErc20BridgeableMod() internal view returns (IERC20BridgeableMod) { - return IERC20BridgeableMod(DIAMOND_STORAGE_SLOT); - } - - /** - * @notice Mints tokens cross-chain. - * @param _recipient The address to mint tokens to. - * @param _amount The amount of tokens to mint. - */ - function crosschainMint(address _recipient, uint256 _amount) external { - _getErc20BridgeableMod().crosschainMint(_recipient, _amount); - } - - /** - * @notice Burns tokens cross-chain. - * @param _burner The address burning tokens. - * @param _amount The amount of tokens to burn. - */ - function crosschainBurn(address _burner, uint256 _amount) external { - _getErc20BridgeableMod().crosschainBurn(_burner, _amount); - } -}`} - - -## Best Practices - - -- Ensure only addresses with the `trusted-bridge` role can call `crosschainMint` and `crosschainBurn`. -- Handle `AccessControlUnauthorizedAccount` and `ERC20InvalidBridgeAccount` errors appropriately in client applications. -- Be aware that token supply changes are managed by external trusted bridge accounts. - - -## Integration Notes - - -This module relies on the `AccessControl` and `ERC20` storage layouts within the diamond. The `getAccessControlStorage` and `getERC20Storage` helper functions provide direct access to these critical storage areas. The `trusted-bridge` role is defined within the `AccessControl` storage. Facets interacting with this module should use the provided helper functions to access storage to ensure consistency and compatibility. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json deleted file mode 100644 index 710c86dd..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20 Bridgeable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC20/ERC20Bridgeable", - "description": "ERC-20 Bridgeable extension for ERC-20 tokens." - } -} diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx deleted file mode 100644 index 6a5c6ae8..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ /dev/null @@ -1,340 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "Manage ERC-20 token allowances with EIP-2612 permit functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC-20 token allowances with EIP-2612 permit functionality. - - - -- Implements EIP-2612 `permit` function for gasless approvals. -- Provides `nonces` and `DOMAIN_SEPARATOR` to facilitate off-chain signature generation. -- Integrates seamlessly with the Compose diamond proxy pattern. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant on-chain signature-based approvals for ERC-20 tokens. This allows users to grant token allowances to spenders without needing to sign an explicit ERC-20 `approve` transaction, reducing gas costs and improving user experience. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Permit, ERC20PermitFacet} from "@compose/contracts/src/facets/ERC20PermitFacet.sol"; -import {IDiamondCut, DiamondInit} from "@compose/contracts/src/interfaces/IDiamond.sol"; - -contract MyDiamondInit is DiamondInit { - function init(IDiamondCut.FacetCut[] memory _diamondCut) public override { - // ... other initializations ... - - // Add ERC20PermitFacet - address erc20PermitFacetAddress = address(new ERC20PermitFacet()); - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut( - erc20PermitFacetAddress, - IDiamondCut.FacetCutAction.ADD, - IDiamondCut.getSelectors(erc20PermitFacetAddress) - ); - - // Assuming diamondCut is available from DiamondInit - super.init(cuts); - } -} - -contract UserInteraction { - ERC20PermitFacet public erc20PermitFacet; // Assume this is deployed and selectors added to diamond - IERC20Permit public token; // The ERC-20 token contract - - function grantPermit(address spender, uint256 amount, uint256 deadline, bytes calldata signature) public { - // User would have previously called nonces() and DOMAIN_SEPARATOR() to construct permit data - // and then signed it off-chain. - erc20PermitFacet.permit(token, spender, amount, deadline, signature); - } -}`} - - -## Best Practices - - -- Ensure the `ERC20PermitFacet` is correctly deployed and its selectors are added to the diamond's routing. -- Users must obtain the `DOMAIN_SEPARATOR` and their current `nonce` from the diamond before constructing and signing the permit message. -- Carefully manage the `deadline` parameter to prevent permits from expiring prematurely or remaining valid indefinitely. - - -## Security Considerations - - -The `permit` function is permissionless and directly modifies allowances. Ensure the signature is valid and the nonce is current to prevent replay attacks or unauthorized allowance changes. The `deadline` parameter is critical for limiting the validity of permits. Users must verify the `DOMAIN_SEPARATOR` to ensure they are signing for the correct contract and chain. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx deleted file mode 100644 index 40571140..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ /dev/null @@ -1,284 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "ERC-2612 Permit logic for token allowances." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2612 Permit logic for token allowances. - - - -- Implements ERC-2612 Permit functionality, allowing off-chain signed allowances. -- Provides a standard `DOMAIN_SEPARATOR` for signature domain separation. -- Includes necessary storage for permit logic. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the necessary functions and storage structures to implement ERC-20 Permit (EIP-2612) functionality. It enables users to grant token allowances via signed messages, enhancing user experience by reducing the need for direct on-chain interactions for every allowance setting. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20PermitMod} from "@compose/modules/erc20/permit/IERC20PermitMod.sol"; - -contract MyTokenFacet { - IERC20PermitMod public immutable erc20PermitMod; - - constructor(address _erc20PermitModAddress) { - erc20PermitMod = IERC20PermitMod(_erc20PermitModAddress); - } - - /** - * @notice Sets an allowance for a spender using an ERC-2612 permit. - * @param owner The owner of the tokens. - * @param spender The address that will be allowed to spend the tokens. - * @param value The amount of tokens that the spender is allowed to spend. - * @param deadline The deadline for the permit. - * @param v The v component of the signature. - * @param r The r component of the signature. - * @param s The s component of the signature. - */ - function setAllowanceWithPermit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // The permit function emits the Approval event internally if successful. - erc20PermitMod.permit(owner, spender, value, deadline, v, r, s); - // Facet logic to update internal allowance tracking or other state can go here. - } -}`} - - -## Best Practices - - -- Ensure the `permit` function is called by a facet that correctly handles the `Approval` event emission as specified by the module. -- Implement robust signature verification logic within the facet calling the `permit` function if additional checks are required beyond the module's validation. -- Be mindful of the `deadline` parameter to prevent stale permit usage. - - -## Integration Notes - - -The `ERC20PermitMod` requires access to the diamond's storage to manage its internal state, including the domain separator and permit data. Facets interacting with this module should ensure they are correctly initialized with the module's address and that they correctly handle the `Approval` event emitted by the `permit` function. The `DOMAIN_SEPARATOR` is computed based on the diamond's address and chain ID, ensuring uniqueness and replay protection. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json deleted file mode 100644 index 2282a192..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20 Permit", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC20/ERC20Permit", - "description": "ERC-20 Permit extension for ERC-20 tokens." - } -} diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json deleted file mode 100644 index 32857d9d..00000000 --- a/website/docs/library/token/ERC20/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC20", - "description": "ERC-20 fungible token implementations." - } -} diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx deleted file mode 100644 index 4c0f3194..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ /dev/null @@ -1,525 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "Manage ERC-6909 compliant token balances and operator permissions." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC-6909 compliant token balances and operator permissions. - - - -- Implements core ERC-6909 functionality for token management. -- Supports transfer, transferFrom, approve, and setOperator operations. -- Provides view functions for querying balances, allowances, and operator status. -- Emits standard ERC-6909 events for state changes. - - -## Overview - -The ERC6909Facet provides functionality for managing fungible or non-fungible token balances and operator approvals according to the ERC-6909 standard. It enables transfers, balance queries, allowance checks, and operator management directly within the diamond proxy. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Facet} from "@compose-protocol/diamond/contracts/facets/ERC6909/IERC6909Facet.sol"; -import {DiamondProxy} from "@compose-protocol/diamond/contracts/DiamondProxy.sol"; - -contract ERC6909Consumer { - IERC6909Facet public erc6909Facet; - - constructor(address _diamondProxyAddress) { - erc6909Facet = IERC6909Facet(_diamondProxyAddress); - } - - function consumeERC6909(address _receiver, uint256 _amount) external { - // Assume token ID is known or managed elsewhere - uint256 tokenId = 1; - erc6909Facet.transfer(tokenId, msg.sender, _receiver, _amount); - } - - function getBalance(address _owner, uint256 _tokenId) external view returns (uint256) { - return erc6909Facet.balanceOf(_owner, _tokenId); - } -}`} - - -## Best Practices - - -- Integrate the `ERC6909Facet` into your diamond via deployment scripts. Ensure the facet's functions are correctly added to the diamond's facet registry. -- Use the `balanceOf`, `allowance`, and `isOperator` functions for read-only queries to understand token ownership and permissions. -- Handle `Transfer` and `Approval` events emitted by the facet to maintain off-chain state or trigger further actions. - - -## Security Considerations - - -Ensure appropriate access control is configured at the diamond level for functions like `transfer`, `transferFrom`, and `approve` if they are not intended to be universally accessible. Validate receiver and sender addresses to prevent sending tokens to unintended recipients. Be mindful of potential reentrancy if downstream logic triggered by events is not carefully implemented. - - -
- -
- - diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx deleted file mode 100644 index 3f593c41..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ /dev/null @@ -1,518 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "ERC-6909 minimal multi-token logic and storage." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-6909 minimal multi-token logic and storage. - - - -- Implements core ERC-6909 functions: `transfer`, `approve`, `mint`, `burn`, `setOperator`. -- Manages token balances and allowances for multiple token IDs. -- Supports operator functionality for delegated token management. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the core logic and storage for implementing the ERC-6909 standard, enabling minimal multi-token functionality within a Compose diamond. It handles token approvals, transfers, minting, and burning, abstracting complex state management into a single, composable unit. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Mod} from "@compose/modules/ERC6909Mod.sol"; -import {IDiamond} from "@compose/core/IDiamond.sol"; - -contract MyERC6909Facet { - IERC6909Mod private constant ERC6909 = IERC6909Mod(address(this)); - - function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { - ERC6909.transfer(_from, _to, _id, _amount); - } - - function approveSpender(address _spender, uint256 _id, uint256 _amount) external { - ERC6909.approve(_spender, _id, _amount); - } - - function mintNewTokens(address _to, uint256 _id, uint256 _amount) external { - ERC6909.mint(_to, _id, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC6909Mod facet is correctly initialized with the appropriate storage slot. -- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` in calling facets. -- Be aware that operator permissions grant broad access to token balances; manage operator roles carefully. - - -## Integration Notes - - -The ERC6909Mod module utilizes a specific storage slot (defined by `STORAGE_POSITION`) for its internal `ERC6909Storage` struct. Facets interacting with this module should use the `getStorage` function to access the storage pointer. This ensures that all ERC-6909 operations correctly reference and modify the shared state, maintaining atomicity and consistency across the diamond. - - -
- -
- - diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json deleted file mode 100644 index bff34320..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC6909/ERC6909", - "description": "ERC-6909 minimal multi-token implementations." - } -} diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json deleted file mode 100644 index aba18a4d..00000000 --- a/website/docs/library/token/ERC6909/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC6909", - "description": "ERC-6909 minimal multi-token implementations." - } -} diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx deleted file mode 100644 index 45885d39..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ /dev/null @@ -1,212 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "Burn ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC721 tokens within a Compose diamond. - - - -- Destroys ERC721 tokens, removing them from circulation and enumeration. -- Leverages the diamond storage pattern for efficient access to token data. -- Emits `Transfer` and `ApprovalForAll` events to signal token destruction. - - -## Overview - -The ERC721BurnFacet allows for the destruction of ERC721 tokens managed by a Compose diamond. It integrates with the diamond's storage pattern to access and modify token ownership and enumeration data, ensuring that burned tokens are permanently removed from the system. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; - -// Assume ERC721BurnFacet and DiamondInit are deployed and cut into the diamond - -// Example of burning a token -contract BurnExample { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 tokenId) public { - // Select the burn function from the ERC721BurnFacet - // The selector for burn is 0x18d739c1 - (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(bytes4(0x18d739c1), tokenId)); - require(success, "Burn failed"); - } -}`} - - -## Best Practices - - -- Ensure the ERC721BurnFacet is correctly cut into the diamond proxy with appropriate selectors. -- Verify that the `owner` of the token or an `operator` approved for the token has sufficient approval to burn the token, as enforced by the underlying ERC721 logic. - - -## Security Considerations - - -This facet relies on the underlying ERC721 implementation for ownership and approval checks. Ensure that the caller has the necessary permissions (owner or approved operator) to burn the specified token. The `burn` function does not perform reentrancy checks; ensure the caller is not a malicious contract designed for reentrancy attacks. Input validation for `tokenId` is handled by the underlying ERC721 logic. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx deleted file mode 100644 index c65cc6bb..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx +++ /dev/null @@ -1,664 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "Manages ERC-721 token ownership, transfers, and approvals." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-721 token ownership, transfers, and approvals. - - - -- Implements core ERC-721 functions including name, symbol, balanceOf, ownerOf, and tokenURI. -- Supports token approvals via `approve` and `setApprovalForAll`. -- Includes internal and external transfer functions for flexible token movement. - - -## Overview - -The ERC721Facet provides the core functionality for managing non-fungible tokens (NFTs) within a Compose diamond. It handles token ownership, retrieval of token metadata, and manages approvals for token transfers, enabling composable NFT logic. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose-protocol/diamond/contracts/facets/ERC721/IERC721Facet.sol"; - -contract NftManager { - IERC721Facet private immutable _erc721Facet; - - constructor(address _diamondAddress) { - _erc721Facet = IERC721Facet(_diamondAddress); - } - - function getNftName() external view returns (string memory) { - return _erc721Facet.name(); - } - - function getNftSymbol() external view returns (string memory) { - return _erc721Facet.symbol(); - } - - function getTokenOwner(uint256 tokenId) external view returns (address) { - return _erc721Facet.ownerOf(tokenId); - } - - function transferMyToken(address to, uint256 tokenId) external { - _erc721Facet.transferFrom(msg.sender, to, tokenId); - } -}`} - - -## Best Practices - - -- Initialize the diamond with the ERC721Facet to enable NFT functionality. -- Ensure correct ownership and approval checks are performed before attempting transfers. -- Use `safeTransferFrom` for transfers to unknown or untrusted recipient contracts. - - -## Security Considerations - - -Input validation is critical for all functions to prevent errors and potential exploits. Ensure that `ownerOf` checks are performed before transfers and that approvals are properly managed. Reentrancy is mitigated by the diamond proxy pattern and internal function design. Use of `safeTransferFrom` is recommended when interacting with potentially untrusted recipient contracts. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx deleted file mode 100644 index b2969345..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "Internal logic for ERC-721 token management." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Internal logic for ERC-721 token management. - - - -- Provides atomic operations for minting, transferring, and burning ERC-721 tokens. -- Manages ERC-721 token ownership and approvals within diamond storage. -- Abstracts complex ERC-721 state management into reusable internal logic. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod provides core internal logic for managing ERC-721 tokens within a Compose diamond. It encapsulates the state and operations for minting, transferring, burning, and managing approvals, ensuring consistency and reusability across different ERC-721 facets. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod } from "@compose/modules/erc721/IERC721Mod.sol"; -import {ERC721ModStorage} from "@compose/modules/erc721/ERC721ModStorage.sol"; - -contract MyERC721Facet { - IERC721Mod public immutable erc721Mod; - - constructor(address _diamondAddress) { - erc721Mod = IERC721Mod(_diamondAddress); - } - - function safeMint(address _to, uint256 _tokenId) external { - // Assume ownership checks are handled by access control - ERC721ModStorage.ERC721Storage storage erc721Storage = erc721Mod.getStorage(); - erc721Mod.mint(_to, _tokenId); - } - - function safeTransfer(address _from, address _to, uint256 _tokenId) external { - // Assume ownership and approval checks are handled internally by transferFrom - erc721Mod.transferFrom(_from, _to, _tokenId); - } -}`} - - -## Best Practices - - -- Ensure proper access control is implemented in facets calling these internal functions. -- Handle ERC721Mod custom errors (`ERC721IncorrectOwner`, `ERC721NonexistentToken`, etc.) in calling facets. -- Be aware that `setMetadata` is not described and may require explicit implementation in a facet. - - -## Integration Notes - - -The ERC721Mod utilizes a predefined storage slot for its `ERC721Storage` struct. Facets integrating with this module can access this storage directly via the `getStorage()` function. Any modifications made to the ERC-721 state through the module's functions (e.g., `mint`, `transferFrom`, `burn`) are persistent and visible across all facets interacting with the diamond's ERC-721 functionality. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json deleted file mode 100644 index 57484cb7..00000000 --- a/website/docs/library/token/ERC721/ERC721/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC721/ERC721", - "description": "ERC-721 non-fungible token implementations." - } -} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index 43948de7..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,221 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "Handles burning of ERC721 tokens and maintains enumeration." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Handles burning of ERC721 tokens and maintains enumeration. - - - -- Enables burning of ERC721 tokens. -- Maintains internal token enumeration consistency after burns. -- Emits `Transfer` event for burned tokens (from owner to address(0)). - - -## Overview - -The ERC721EnumerableBurnFacet provides the functionality to burn ERC721 tokens within a Compose diamond. It ensures that token destruction is properly recorded and that the internal enumeration of tokens remains accurate after a burn operation. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableBurnFacet} from "@compose-protocol/diamond/facets/ERC721/IERC721EnumerableBurnFacet.sol"; - -contract ExampleUsage { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 _tokenId) external { - // Assume appropriate access control is handled by the diamond proxy - IERC721EnumerableBurnFacet(diamondAddress).burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the diamond proxy is configured to route `burn` calls to this facet. -- Implement necessary access control mechanisms within the diamond to authorize token burning. - - -## Security Considerations - - -Access control for burning tokens must be strictly enforced at the diamond proxy level. The `burn` function relies on the caller having the necessary permissions to burn the specified token. The facet itself does not implement specific access control checks beyond those inherent to ERC721 ownership and approvals. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx deleted file mode 100644 index 85e76a9d..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,739 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "Enumerable ERC-721 token management" -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enumerable ERC-721 token management - - - -- Provides `totalSupply`, `balanceOf`, and `ownerOf` for standard ERC-721 queries. -- Enables iteration over an owner's tokens using `tokenOfOwnerByIndex`. -- Supports standard ERC-721 approval and transfer mechanisms, including safe transfers. - - -## Overview - -This facet provides full ERC-721 enumerable functionality, allowing querying of token metadata, ownership details, and total supply. It extends the standard ERC-721 interface by enabling iteration over tokens owned by an address and tracking token ownership by index. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableFacet} from "@compose/diamond/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; - -contract ERC721EnumerableConsumer { - IERC721EnumerableFacet private immutable _erc721; - - constructor(address diamondAddress) { - _erc721 = IERC721EnumerableFacet(diamondAddress); - } - - function getERC721Name() external view returns (string memory) { - return _erc721.name(); - } - - function getTokenOwner(uint256 tokenId) external view returns (address) { - return _erc721.ownerOf(tokenId); - } - - function getTokensOwnedBy(address owner) external view returns (uint256[] memory) { - uint256 count = _erc721.balanceOf(owner); - uint256[] memory tokenIds = new uint256[](count); - for (uint256 i = 0; i < count; i++) { - tokenIds[i] = _erc721.tokenOfOwnerByIndex(owner, i); - } - return tokenIds; - } -}`} - - -## Best Practices - - -- Initialize the diamond with this facet to enable ERC-721 enumerable features. -- Use `tokenOfOwnerByIndex` carefully, as it requires knowledge of the owner's token balance to iterate effectively. -- Ensure that any custom ERC-721 implementations correctly integrate with the internal transfer logic to maintain enumerable state. - - -## Security Considerations - - -Access control for `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` functions must be rigorously enforced by the diamond's access control mechanism. The `internalTransferFrom` function is a critical internal component; ensure it is only called by authorized functions to prevent state manipulation. Reentrancy is a concern for transfer functions; ensure proper checks are in place, especially for `safeTransferFrom` when interacting with external contracts. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx deleted file mode 100644 index e1ced973..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,344 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "Manages enumerable ERC721 tokens within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages enumerable ERC721 tokens within a diamond. - - - -- Manages the addition and removal of tokens from enumeration lists during mint and burn operations. -- Provides internal functions for minting, burning, and transferring ERC721 tokens while maintaining enumeration state. -- Utilizes inline assembly via `getStorage` to access its internal storage struct efficiently. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the core logic for managing enumerable ERC721 tokens. It ensures that minted tokens are added to internal tracking lists and burned tokens are removed, maintaining accurate token counts and ownership history. Integrating this module allows facets to seamlessly implement ERC721 functionality with enumeration support. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC721EnumerableMod} from "./ERC721EnumerableMod.sol"; - -contract MyERC721Facet { - ERC721EnumerableMod internal enumerableMod; - - function initialize(address _diamondAddress) external { - // Assuming ERC721EnumerableMod is deployed and its address is known - // In a real scenario, this address would likely be passed in or retrieved from a registry. - address enumerableModAddress = _diamondAddress; // Placeholder - enumerableMod = ERC721EnumerableMod(enumerableModAddress); - } - - function mintToken(address _to, uint256 _tokenId) external { - enumerableMod.mint(_to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - enumerableMod.burn(_tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - enumerableMod.transferFrom(_from, _to, _tokenId); - } -}`} - - -## Best Practices - - -- Ensure proper authorization checks are implemented in facets calling `mint`, `burn`, and `transferFrom` functions. -- Handle custom errors like `ERC721NonexistentToken`, `ERC721InvalidSender`, and `ERC721InvalidReceiver` in calling facets for robust error management. -- Be aware of the storage slot used by `ERC721EnumerableMod` and avoid conflicts when adding other facets. - - -## Integration Notes - - -The `ERC721EnumerableMod` relies on a predefined storage slot for its `ERC721EnumerableStorage` struct. Facets integrating this module must ensure that this slot is not overwritten by other facets. The `getStorage` function uses inline assembly to directly access this storage slot, making the module's state available to any facet that calls it. Changes to the enumeration lists are immediately reflected in the diamond's state. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json deleted file mode 100644 index c80a4b77..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-721 Enumerable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC721/ERC721Enumerable", - "description": "ERC-721 Enumerable extension for ERC-721 tokens." - } -} diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json deleted file mode 100644 index 208a6407..00000000 --- a/website/docs/library/token/ERC721/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/ERC721", - "description": "ERC-721 non-fungible token implementations." - } -} diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx deleted file mode 100644 index 77914fee..00000000 --- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx +++ /dev/null @@ -1,188 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "Manages and retrieves royalty information per token." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages and retrieves royalty information per token. - - - -- Implements ERC-2981 `royaltyInfo` standard. -- Supports default and token-specific royalty configurations. -- Calculates royalty amounts based on sale price and basis points. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard, allowing Compose diamonds to specify and retrieve royalty details for token sales. It supports both default royalties and token-specific overrides, ensuring proper distribution of sale proceeds. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyFacet} from "@compose/diamond/facets/Royalty/IRoyaltyFacet.sol"; - -contract RoyaltyConsumer { - address immutable diamondProxy; - bytes4 constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; - - constructor(address _diamondProxy) { - diamondProxy = _diamondProxy; - } - - function getSaleRoyalty(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 royaltyAmount) { - (receiver, royaltyAmount) = IRoyaltyFacet(diamondProxy).royaltyInfo(tokenId, salePrice); - } -}`} - - -## Best Practices - - -- Initialize default royalties during diamond deployment or via an admin function. -- Token-specific royalties should be managed by the NFT contract facet, calling into this facet's setter functions if available (or directly setting storage if access is granted). -- Ensure the `STORAGE_POSITION` for royalty data is correctly defined and unique. - - -## Security Considerations - - -Access to modify royalty settings should be restricted to authorized roles (e.g., owner, admin) to prevent malicious manipulation of royalty distributions. Ensure input validation for `tokenId` and `salePrice` is handled appropriately by calling facets. The `getStorage` function uses inline assembly; verify the `STORAGE_POSITION` is correctly set to avoid data corruption. - - -
- -
- - diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx deleted file mode 100644 index 138c6ace..00000000 --- a/website/docs/library/token/Royalty/RoyaltyMod.mdx +++ /dev/null @@ -1,382 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "Manages ERC-2981 royalties with default and token-specific settings." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-2981 royalties with default and token-specific settings. - - - -- Supports ERC-2981 standard for on-chain royalties. -- Allows setting both default and token-specific royalty configurations. -- Provides fallback logic from token-specific to default royalties. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides robust ERC-2981 royalty management, enabling both default royalty settings and token-specific overrides. It ensures compatibility with the ERC-2981 standard by implementing the `royaltyInfo` function, which queries token-specific or fallback default royalty information. This composable approach allows diamonds to easily integrate royalty logic without complex inheritance. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "path/to/IRoyaltyMod.sol"; - -contract RoyaltyFacet { - // Assume IRoyaltyMod is imported and diamond storage is accessed correctly - // For example, via a diamond storage contract. - IRoyaltyMod internal royaltyMod; - - constructor(address _diamondAddress) { - // Initialize royaltyMod with the diamond address - // This is a simplified example; actual initialization depends on diamond proxy setup. - royaltyMod = IRoyaltyMod(_diamondAddress); - } - - /** - * @notice Sets a default royalty for all tokens. - * @param _receiver The address to receive royalty payments. - * @param _feeBasisPoints The royalty fee in basis points (e.g., 100 for 1%). - */ - function setDefaultRoyalty(address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setDefaultRoyalty(_receiver, _feeBasisPoints); - } - - /** - * @notice Sets a specific royalty for a token ID. - * @param _tokenId The ID of the token to set royalty for. - * @param _receiver The address to receive royalty payments. - * @param _feeBasisPoints The royalty fee in basis points. - */ - function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); - } - - /** - * @notice Queries royalty information for a given token ID and sale price. - * @param _tokenId The ID of the token. - * @param _salePrice The sale price of the token. - * @return receiver The address to receive the royalty payment. - * @return royaltyAmount The calculated royalty amount. - */ - function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { - (receiver, royaltyAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); - return (receiver, royaltyAmount); - } - - /** - * @notice Resets royalty information for a specific token to use the default. - * @param _tokenId The ID of the token to reset. - */ - function resetTokenRoyalty(uint256 _tokenId) external { - royaltyMod.resetTokenRoyalty(_tokenId); - } - - /** - * @notice Deletes the default royalty information. - */ - function deleteDefaultRoyalty() external { - royaltyMod.deleteDefaultRoyalty(); - } -} -`} - - -## Best Practices - - -- Validate receiver addresses and fee basis points to prevent unexpected royalty distributions using the module's custom errors. -- Use `resetTokenRoyalty` to revert token-specific royalties to the default, ensuring predictable fallback behavior. -- Be aware that calling `deleteDefaultRoyalty` will cause `royaltyInfo` to return `(address(0), 0)` for tokens without specific royalties. - - -## Integration Notes - - -This module interacts with diamond storage at a predefined slot to manage royalty data. Facets can access this data using the `getStorage` function, which uses inline assembly to read from the diamond's storage. Changes to default or token-specific royalties are immediately reflected in subsequent calls to `royaltyInfo`. Ensure that the storage slot for royalty data is not utilized by other facets to avoid conflicts. - - -
- -
- - diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json deleted file mode 100644 index 615503af..00000000 --- a/website/docs/library/token/Royalty/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Royalty", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token/Royalty", - "description": "ERC-2981 royalty standard implementations." - } -} diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json deleted file mode 100644 index 7eca87aa..00000000 --- a/website/docs/library/token/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Token Standards", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/token", - "description": "Token standard implementations for Compose diamonds." - } -} diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx deleted file mode 100644 index cfc0b343..00000000 --- a/website/docs/library/utils/NonReentrancyMod.mdx +++ /dev/null @@ -1,137 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "Prevent reentrant calls within facets." -gitSource: "https://github.com/maxnorm/Compose/blob/75e2e68f9cd44e6a24767abe937e3f91886c823f/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Prevent reentrant calls within facets. - - - -- Provides `enter()` and `exit()` functions to manage reentrancy guards. -- Utilizes a simple `uint256` as storage for the reentrancy state, allowing for composability. -- Emits a `Reentrancy` error if a reentrant call is detected. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod provides essential functions to prevent reentrant function calls within your diamond facets. By managing an internal state, it ensures that a function cannot be re-entered before its initial execution completes, safeguarding against unexpected state changes and potential exploits. - ---- - -## Storage - -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose/modules/NonReentrancyMod.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 internal _reentrancyState; - - /** - * @notice Performs an action that must not be reentrant. - */ - function performAction() external { - _reentrancyState.enter(); - - // ... perform sensitive operations ... - - _reentrancyState.exit(); - } -}`} - - -## Best Practices - - -- Always pair `enter()` with a corresponding `exit()` call, ideally using `try...finally` or ensuring `exit()` is called even if internal operations revert. -- Place `enter()` at the very beginning of the function and `exit()` at the very end to establish the widest possible protection. -- Consider the implications of reentrancy on all external calls made within a protected function. - - -## Integration Notes - - -The `NonReentrancyMod` expects to manage its state within a `uint256` slot. Facets integrating this module should ensure that a `uint256` is allocated in their storage layout for the reentrancy state. The `enter` and `exit` functions operate directly on this `uint256` slot, making it a self-contained and composable guard. - - -
- -
- - diff --git a/website/docs/library/utils/_category_.json b/website/docs/library/utils/_category_.json deleted file mode 100644 index b01ae58a..00000000 --- a/website/docs/library/utils/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Utilities", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "slug": "/docs/library/utils", - "description": "Utility libraries and helpers for diamond development." - } -} From 2ca753d9e6a30e3ab63f4697f17f272c2f6c0674 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sun, 21 Dec 2025 21:28:30 +0000 Subject: [PATCH 49/68] docs: auto-generate docs pages from NatSpec --- website/docs/library/_category_.json | 10 + .../AccessControl/AccessControlFacet.mdx | 547 +++++++++++++ .../access/AccessControl/AccessControlMod.mdx | 442 +++++++++++ .../access/AccessControl/_category_.json | 10 + .../library/access/AccessControl/index.mdx | 14 + .../AccessControlPausableFacet.mdx | 371 +++++++++ .../AccessControlPausableMod.mdx | 374 +++++++++ .../AccessControlPausable/_category_.json | 10 + .../access/AccessControlPausable/index.mdx | 14 + .../AccessControlTemporalFacet.mdx | 456 +++++++++++ .../AccessControlTemporalMod.mdx | 477 ++++++++++++ .../AccessControlTemporal/_category_.json | 10 + .../access/AccessControlTemporal/index.mdx | 14 + .../docs/library/access/Owner/OwnerFacet.mdx | 211 +++++ .../docs/library/access/Owner/OwnerMod.mdx | 257 ++++++ .../docs/library/access/Owner/_category_.json | 10 + website/docs/library/access/Owner/index.mdx | 14 + .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 286 +++++++ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 297 +++++++ .../access/OwnerTwoSteps/_category_.json | 10 + .../library/access/OwnerTwoSteps/index.mdx | 14 + website/docs/library/access/_category_.json | 10 + website/docs/library/access/index.mdx | 14 + .../docs/library/diamond/DiamondCutFacet.mdx | 420 ++++++++++ .../docs/library/diamond/DiamondCutMod.mdx | 401 ++++++++++ .../library/diamond/DiamondLoupeFacet.mdx | 252 ++++++ website/docs/library/diamond/DiamondMod.mdx | 243 ++++++ website/docs/library/diamond/_category_.json | 10 + .../diamond/example/ExampleDiamond.mdx | 130 +++ .../library/diamond/example/_category_.json | 10 + .../docs/library/diamond/example/index.mdx | 14 + website/docs/library/diamond/index.mdx | 14 + website/docs/library/index.mdx | 14 + .../interfaceDetection/ERC165/ERC165Mod.mdx | 152 ++++ .../interfaceDetection/ERC165/_category_.json | 10 + .../interfaceDetection/ERC165/index.mdx | 14 + .../interfaceDetection/_category_.json | 10 + .../docs/library/interfaceDetection/index.mdx | 14 + .../library/token/ERC1155/ERC1155Facet.mdx | 674 ++++++++++++++++ .../docs/library/token/ERC1155/ERC1155Mod.mdx | 604 ++++++++++++++ .../library/token/ERC1155/_category_.json | 10 + website/docs/library/token/ERC1155/index.mdx | 14 + .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 248 ++++++ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 573 ++++++++++++++ .../library/token/ERC20/ERC20/ERC20Mod.mdx | 422 ++++++++++ .../library/token/ERC20/ERC20/_category_.json | 10 + .../docs/library/token/ERC20/ERC20/index.mdx | 14 + .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 434 +++++++++++ .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 437 +++++++++++ .../ERC20/ERC20Bridgeable/_category_.json | 10 + .../token/ERC20/ERC20Bridgeable/index.mdx | 14 + .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 340 ++++++++ .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 277 +++++++ .../token/ERC20/ERC20Permit/_category_.json | 10 + .../library/token/ERC20/ERC20Permit/index.mdx | 14 + .../docs/library/token/ERC20/_category_.json | 10 + website/docs/library/token/ERC20/index.mdx | 14 + .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 526 +++++++++++++ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 517 ++++++++++++ .../token/ERC6909/ERC6909/_category_.json | 10 + .../library/token/ERC6909/ERC6909/index.mdx | 14 + .../library/token/ERC6909/_category_.json | 10 + website/docs/library/token/ERC6909/index.mdx | 14 + .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 210 +++++ .../token/ERC721/ERC721/ERC721Facet.mdx | 662 ++++++++++++++++ .../library/token/ERC721/ERC721/ERC721Mod.mdx | 358 +++++++++ .../token/ERC721/ERC721/_category_.json | 10 + .../library/token/ERC721/ERC721/index.mdx | 14 + .../ERC721EnumerableBurnFacet.mdx | 217 ++++++ .../ERC721EnumerableFacet.mdx | 737 ++++++++++++++++++ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 342 ++++++++ .../ERC721/ERC721Enumerable/_category_.json | 10 + .../token/ERC721/ERC721Enumerable/index.mdx | 14 + .../docs/library/token/ERC721/_category_.json | 10 + website/docs/library/token/ERC721/index.mdx | 14 + .../library/token/Royalty/RoyaltyFacet.mdx | 191 +++++ .../docs/library/token/Royalty/RoyaltyMod.mdx | 354 +++++++++ .../library/token/Royalty/_category_.json | 10 + website/docs/library/token/Royalty/index.mdx | 14 + website/docs/library/token/_category_.json | 10 + website/docs/library/token/index.mdx | 14 + .../docs/library/utils/NonReentrancyMod.mdx | 139 ++++ website/docs/library/utils/_category_.json | 10 + website/docs/library/utils/index.mdx | 14 + 84 files changed, 14154 insertions(+) create mode 100644 website/docs/library/_category_.json create mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx create mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx create mode 100644 website/docs/library/access/AccessControl/_category_.json create mode 100644 website/docs/library/access/AccessControl/index.mdx create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx create mode 100644 website/docs/library/access/AccessControlPausable/_category_.json create mode 100644 website/docs/library/access/AccessControlPausable/index.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json create mode 100644 website/docs/library/access/AccessControlTemporal/index.mdx create mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx create mode 100644 website/docs/library/access/Owner/OwnerMod.mdx create mode 100644 website/docs/library/access/Owner/_category_.json create mode 100644 website/docs/library/access/Owner/index.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json create mode 100644 website/docs/library/access/OwnerTwoSteps/index.mdx create mode 100644 website/docs/library/access/_category_.json create mode 100644 website/docs/library/access/index.mdx create mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx create mode 100644 website/docs/library/diamond/DiamondCutMod.mdx create mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx create mode 100644 website/docs/library/diamond/DiamondMod.mdx create mode 100644 website/docs/library/diamond/_category_.json create mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx create mode 100644 website/docs/library/diamond/example/_category_.json create mode 100644 website/docs/library/diamond/example/index.mdx create mode 100644 website/docs/library/diamond/index.mdx create mode 100644 website/docs/library/index.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json create mode 100644 website/docs/library/interfaceDetection/ERC165/index.mdx create mode 100644 website/docs/library/interfaceDetection/_category_.json create mode 100644 website/docs/library/interfaceDetection/index.mdx create mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx create mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx create mode 100644 website/docs/library/token/ERC1155/_category_.json create mode 100644 website/docs/library/token/ERC1155/index.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20/index.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Permit/index.mdx create mode 100644 website/docs/library/token/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC20/index.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC6909/ERC6909/index.mdx create mode 100644 website/docs/library/token/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC6909/index.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721/index.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/index.mdx create mode 100644 website/docs/library/token/ERC721/_category_.json create mode 100644 website/docs/library/token/ERC721/index.mdx create mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx create mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx create mode 100644 website/docs/library/token/Royalty/_category_.json create mode 100644 website/docs/library/token/Royalty/index.mdx create mode 100644 website/docs/library/token/_category_.json create mode 100644 website/docs/library/token/index.mdx create mode 100644 website/docs/library/utils/NonReentrancyMod.mdx create mode 100644 website/docs/library/utils/_category_.json create mode 100644 website/docs/library/utils/index.mdx diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json new file mode 100644 index 00000000..04125e1e --- /dev/null +++ b/website/docs/library/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Library", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/index" + } +} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx new file mode 100644 index 00000000..232d9d68 --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -0,0 +1,547 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Manage roles and permissions within the diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within the diamond. + + + +- Role-based access control system. +- Support for granting, revoking, and renouncing roles. +- Batch operations for efficient role management. +- Role admin hierarchy for decentralized control. + + +## Overview + +The AccessControlFacet provides a robust, role-based access control mechanism for Compose diamonds. It allows for the definition of roles, assignment of roles to accounts, and enforcement of role-based permissions on function calls. This facet is crucial for securing sensitive operations and managing administrative privileges within the diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondLoupeFacet} from "@compose-diamond/diamond-loupe/contracts/DiamondLoupeFacet.sol"; +import {AccessControlFacet} from "@compose-diamond/access-control/contracts/AccessControlFacet.sol"; + +contract MyDiamond is DiamondLoupeFacet { + // Facet definition can be found in the Compose Diamond template + // ... + + function setAdminRole(address _diamondAdmin, bytes4 _functionSelector, bytes32 _adminRole) external { + AccessControlFacet ac = AccessControlFacet(address(this)); + // Ensure caller is the diamond admin or has the role to change role admins + ac.setRoleAdmin(_adminRole); // This is an example, actual implementation may differ based on role admin logic + } + + function grantAccess(address _account, bytes32 _role) external { + AccessControlFacet ac = AccessControlFacet(address(this)); + ac.grantRole(_role, _account); + } + + function checkPermission(address _account, bytes32 _role) external view returns (bool) { + AccessControlFacet ac = AccessControlFacet(address(this)); + return ac.hasRole(_role, _account); + } +} +`} + + +## Best Practices + + +- Define roles clearly and manage their admin roles strategically. The `DEFAULT_ADMIN_ROLE` typically controls the management of other roles. +- Utilize `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple accounts for a single role. +- Implement role checks directly within functions or use internal helper functions to enforce access control consistently. + + +## Security Considerations + + +Ensure that only authorized accounts can grant or revoke roles, especially for administrative roles. The `AccessControlUnauthorizedAccount` error indicates a caller attempting an action without the necessary role. `AccessControlUnauthorizedSender` is used when an account tries to renounce a role it does not possess. Be mindful of potential reentrancy if facet functions are called within external contracts without proper checks. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx new file mode 100644 index 00000000..833abee0 --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -0,0 +1,442 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Manage roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within a diamond. + + + +- Role-based access control for diamond functions. +- Granting, revoking, and checking of roles for accounts. +- Ability to set and manage administrative roles for other roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControl module provides a robust mechanism for managing role-based access control within Compose diamonds. It enables granular permission management by allowing roles to be granted to accounts and checked against specific functions. This module is crucial for securing diamond functionality and ensuring that only authorized accounts can perform sensitive operations. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose-protocol/diamond-contracts/contracts/modules/access-control/IAccessControl.sol"; + +contract MyFacet { + IAccessControl public accessControl; + + // Assume accessControl is initialized to the diamond's AccessControl facet address + + function performAdminTask() external { + // Check if the caller has the ADMIN_ROLE before proceeding + accessControl.requireRole(accessControl.getStorage().ADMIN_ROLE, msg.sender); + + // ... perform admin task ... + } + + function grantNewRole(address _account, bytes32 _role) external { + accessControl.grantRole(_role, _account); + } +}`} + + +## Best Practices + + +- Use `requireRole` with custom errors for explicit access control checks within facets. +- Store role identifiers (`bytes32`) as constants for maintainability and to prevent typos. +- Ensure the `ADMIN_ROLE` is appropriately protected and managed during diamond upgrades. + + +## Integration Notes + + +The AccessControl module utilizes a dedicated storage slot within the diamond. Facets interact with this module by calling its external functions, such as `grantRole`, `revokeRole`, `hasRole`, and `requireRole`. The `getStorage` function provides direct access to the module's internal storage structure, allowing for programmatic inspection of role assignments and configurations. Changes to roles made through the AccessControl facet are immediately reflected for all other facets interacting with the diamond. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json new file mode 100644 index 00000000..1504700a --- /dev/null +++ b/website/docs/library/access/AccessControl/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/AccessControl/index" + } +} diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx new file mode 100644 index 00000000..73addea3 --- /dev/null +++ b/website/docs/library/access/AccessControl/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Access Control" +description: "Role-based access control (RBAC) pattern." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Role-based access control (RBAC) pattern. + + +_No items in this category yet._ diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..579b3029 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -0,0 +1,371 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Manages role-based access control and pausing functionality for diamond modules." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages role-based access control and pausing functionality for diamond modules. + + + +- Role-specific pausing and unpausing of permissions. +- Granular control over module availability based on role status. +- Reverts with specific errors for unauthorized access or paused roles. + + +## Overview + +The AccessControlPausableFacet extends Compose's access control by introducing role-specific pausing. This allows granular control over module functionality, enabling temporary deactivation of roles without affecting the entire diamond. It provides essential functions for checking pause status, pausing, and unpausing roles, ensuring robust operational control. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {AccessControlPausableFacet} from "@compose/contracts/facets/AccessControl/AccessControlPausableFacet.sol"; +import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; + +contract Deployer { + function deployAccessControlPausable() external { + // Assume diamondCut is an instance of IDiamondCut + IDiamondCut diamondCut; + + // Add the AccessControlPausableFacet + diamondCut.execute(IDiamondCut.DiamondCutArgs( + IDiamondCut.FacetCut[]( + IDiamondCut.FacetCut(address(new AccessControlPausableFacet()), IDiamondCut.FacetCutAction.ADD, new bytes[](0)) + ), + address(0), // No delete action + new bytes[](0) // No init data + )); + + // Example of pausing a role (assuming 'CALLER' is a defined role) + // address accessControlPausableFacetAddress = diamondCut.getFacetAddress(AccessControlPausableFacet.FUNC_PAUSEROLE); + // AccessControlPausableFacet(accessControlPausableFacetAddress).pauseRole("CALLER"); + } +}`} + + +## Best Practices + + +- Integrate this facet to enforce role-based access control with the ability to temporarily disable specific roles. +- Use `requireRoleNotPaused` within other facets to ensure operations are only permitted when their associated roles are active. +- Ensure the diamond's admin or the designated role admin is correctly configured to manage pausing and unpausing operations. + + +## Security Considerations + + +Access to `pauseRole` and `unpauseRole` is restricted to the admin of the respective role, preventing unauthorized pausing. The `requireRoleNotPaused` function ensures that calls to protected functions fail if the caller's role is paused, mitigating reentrancy risks related to paused states. Input validation on role names is crucial. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx new file mode 100644 index 00000000..1b876edb --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -0,0 +1,374 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Manage role-based access control with pausing capabilities." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage role-based access control with pausing capabilities. + + + +- Role-specific pausing: allows granular control over operational suspension. +- Composable with existing access control mechanisms. +- Reverts with specific errors (`AccessControlRolePaused`, `AccessControlUnauthorizedAccount`) for clear error handling. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module extends diamond access control by introducing role-specific pausing. It allows administrators to temporarily halt operations for specific roles, ensuring critical functions can be suspended during emergencies or maintenance without affecting the entire diamond. This composition pattern enhances security and operational flexibility. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { +mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableMod} from "@compose/modules/access-control-pausable/IAccessControlPausableMod.sol"; + +contract MyFacet { + IAccessControlPausableMod public constant ACCESS_CONTROL_PAUSABLE_MOD = IAccessControlPausableMod(0xaddress); // Replace with actual module address + + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + + function pauseMyFeature() external { + // Ensure the caller has the PAUSER_ROLE and the role is not paused + ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(msg.sender, PAUSER_ROLE); + // If the role is not paused, proceed with pausing the feature + ACCESS_CONTROL_PAUSABLE_MOD.pauseRole(PAUSER_ROLE); + } + + function performCriticalOperation() external { + // Ensure the caller has the OPERATOR_ROLE and the role is not paused + ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(msg.sender, OPERATOR_ROLE); + // Perform the critical operation + } +}`} + + +## Best Practices + + +- Use `requireRoleNotPaused` to enforce role presence and ensure the role is not paused before executing sensitive operations. +- Grant the `PAUSER_ROLE` only to trusted administrative accounts. +- Monitor `RolePaused` and `RoleUnpaused` events to track state changes. + + +## Integration Notes + + +This module relies on the underlying Access Control module's storage for role management. The `AccessControlPausableMod` adds its own state to manage paused roles. Facets interacting with this module should be aware that calls to `requireRoleNotPaused` will check both role membership and the paused status of that role. The module's functions are designed to be called directly by facets using the module's interface. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json new file mode 100644 index 00000000..96418b00 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Pausable Access Control", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/AccessControlPausable/index" + } +} diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx new file mode 100644 index 00000000..81a1f58c --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Pausable Access Control" +description: "RBAC with pause functionality." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + RBAC with pause functionality. + + +_No items in this category yet._ diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..6afeadb2 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -0,0 +1,456 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Manages time-bound role assignments within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages time-bound role assignments within a diamond. + + + +- Time-bound role assignments: Roles can be granted with a specific expiration timestamp. +- Admin-controlled temporal roles: Only the designated role admin can grant or revoke temporal roles. +- Expiry checks: Functions are provided to query if a role has expired or to enforce non-expired role requirements. + + +## Overview + +The AccessControlTemporalFacet extends Compose's access control capabilities by introducing time-limited role assignments. It allows administrators to grant roles that automatically expire after a specified timestamp, enhancing dynamic permission management. This facet provides functions to grant, revoke, and check the validity of these temporal roles. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondInit} from "@compose/diamond-init/DiamondInit.sol"; +import {DiamondCutFacet} from "@compose/diamond-cut/DiamondCutFacet.sol"; +import {AccessControlFacet} from "@compose/access-control/AccessControlFacet.sol"; +import {AccessControlTemporalFacet} from "@compose/access-control/AccessControlTemporalFacet.sol"; + +contract DeployDiamond { + function deploy() external { + // ... deployment logic ... + + // Example: Granting a role with expiry + address diamondProxy = address(0x...); // Address of your deployed diamond + address admin = msg.sender; // Assuming msg.sender is the admin + bytes32 role = keccak256("MY_TEMPORARY_ROLE"); + uint64 expiryTimestamp = uint64(block.timestamp) + 3600; // Role expires in 1 hour + + // Call grantRoleWithExpiry via the diamond proxy + (bool success, bytes memory data) = diamondProxy.call( + abi.encodeWithSelector(AccessControlTemporalFacet.grantRoleWithExpiry.selector, + role, + address(1), // address to grant role to + expiryTimestamp + ) + ); + require(success, "Grant role failed"); + + // Example: Checking role validity + bool isValid = AccessControlTemporalFacet(diamondProxy).isRoleExpired(role, address(1)); + require(!isValid, "Role is expired or not granted"); + } +}`} + + +## Best Practices + + +- Only grant temporal roles via the `grantRoleWithExpiry` function to ensure proper event emission and access control checks. +- Utilize `isRoleExpired` or `requireValidRole` to enforce time-bound access control before critical operations. +- Store role expiry timestamps in a manner that is easily retrievable and auditable, leveraging the facet's storage accessors. + + +## Security Considerations + + +This facet relies on the underlying access control mechanism for initial role granting. Ensure that the `admin` of a role is appropriately secured. The `grantRoleWithExpiry` and `revokeTemporalRole` functions are protected by access control, ensuring only the role admin can perform these actions. Input validation on the `expiryTimestamp` is crucial to prevent granting roles with invalid or immediately expired timestamps. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..d3ec6f6e --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -0,0 +1,477 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Manages role grants with time-based expiry." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages role grants with time-based expiry. + + + +- Grants roles with a specified expiry timestamp. +- Automatically checks for role expiry when validating access via `requireValidRole`. +- Provides functions to query role expiry status and revoke temporal roles explicitly. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module extends standard access control by introducing time-bound role assignments. It allows for roles to automatically expire, enhancing security and operational flexibility by ensuring temporary privileges are automatically revoked. This is crucial for managing short-term access needs within a diamond. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { +mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; +import {AccessControlFacet} from "@compose/facets/access-control/AccessControlFacet.sol"; + +contract MyDiamond is IAccessControlTemporalMod { + // Assume Diamond ABI encoder and access control setup + + function grantTempAdmin(address _account, uint64 _expiry) external { + // Assuming AccessControlFacet is deployed and accessible + // and the current caller has the necessary permissions to grant roles + grantRoleWithExpiry(_account, ADMIN_ROLE, _expiry); + } + + function enforceAdminAccess(address _account) external { + // Example of enforcing a temporal role + requireValidRole(_account, ADMIN_ROLE); + // ... proceed with admin actions ... + } + + // Other diamond functions +} +`} + + +## Best Practices + + +- Use `requireValidRole` to enforce temporal role checks before executing sensitive operations. +- Ensure the `_expiry` timestamp provided to `grantRoleWithExpiry` is a future Unix timestamp. +- Implement logic to periodically check and revoke expired roles if automatic revocation via `requireValidRole` is not sufficient for all use cases. + + +## Integration Notes + + +This module interacts with the diamond's storage, specifically the access control and temporal access control state. Facets using this module will call its functions to manage and validate roles. The `requireValidRole` function is designed to be called within other facets to ensure that only accounts with active, non-expired roles can perform certain actions. Invariants related to role granting and revocation must be maintained across all facets that modify access control. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json new file mode 100644 index 00000000..834b0b18 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Temporal Access Control", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/AccessControlTemporal/index" + } +} diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx new file mode 100644 index 00000000..76e7730f --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Temporal Access Control" +description: "Time-limited role-based access control." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Time-limited role-based access control. + + +_No items in this category yet._ diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx new file mode 100644 index 00000000..a47b4435 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -0,0 +1,211 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Manages diamond ownership and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond ownership and transfers. + + + +- Manages diamond ownership and transfers. +- Supports `transferOwnership` and `renounceOwnership`. +- Provides a view function to retrieve the current owner. + + +## Overview + +The OwnerFacet provides essential functions for managing the ownership of a Compose diamond. It allows the current owner to transfer ownership to a new address or renounce ownership entirely, ensuring secure control over the diamond's administrative functions. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; + +contract OwnerUser { + IOwnerFacet ownerFacet; + + constructor(address _diamondAddress) { + ownerFacet = IOwnerFacet(_diamondAddress); + } + + function getOwner() public view returns (address) { + return ownerFacet.owner(); + } + + function transferDiamondOwnership(address _newOwner) public { + ownerFacet.transferOwnership(_newOwner); + } + + function renounceDiamondOwnership() public { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Only the current owner should call `transferOwnership` or `renounceOwnership`. +- Ensure the `_newOwner` address is valid before transferring ownership. +- Use `renounceOwnership` with caution, as it permanently relinquishes control. + + +## Security Considerations + + +Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Malicious actors cannot seize ownership without the owner's explicit action. Setting the owner to `address(0)` effectively renounces ownership, making the contract unownable and administrative functions inaccessible. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx new file mode 100644 index 00000000..e4efb3f6 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -0,0 +1,257 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "Manages ERC-173 contract ownership." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-173 contract ownership. + + + +- Manages ERC-173 ownership state. +- Provides `owner()` to retrieve the current owner's address. +- Includes `requireOwner()` for access control checks. +- Supports ownership transfer and renouncement via `transferOwnership()`. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core functionality for managing ERC-173 contract ownership. It defines the storage layout for the owner and offers functions to retrieve the current owner, transfer ownership, and enforce owner-only access. This is crucial for establishing administrative control within a diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; + +contract MyFacet { + IOwnerMod private immutable _ownerMod; + + constructor(address ownerModAddress) { + _ownerMod = IOwnerMod(ownerModAddress); + } + + /** + * @notice Get the current owner of the contract. + */ + function getCurrentOwner() external view returns (address) { + return _ownerMod.owner(); + } + + /** + * @notice Transfer ownership to a new address. + * @param _newOwner The address of the new owner. + */ + function changeOwner(address _newOwner) external { + // Ensure the caller is the current owner before transferring + _ownerMod.requireOwner(); + _ownerMod.transferOwnership(_newOwner); + } +}`} + + +## Best Practices + + +- Only the owner should call functions that modify ownership (`transferOwnership`, `setContractOwner`). Use `requireOwner()` to enforce this. +- Renounce ownership by setting the `_newOwner` to `address(0)` in `transferOwnership` if administrative control is no longer needed. +- Ensure the `OwnerMod` facet is initialized with the correct initial owner address during deployment. + + +## Integration Notes + + +The `OwnerMod` module utilizes a specific storage slot for its `OwnerModStorage` struct. Facets interacting with ownership should obtain a pointer to this storage using `getStorage()`. The `owner()` function directly reads from this storage. `transferOwnership()` and `setContractOwner()` modify this storage. Invariants related to ownership should be maintained across all facets that interact with or depend on the owner. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json new file mode 100644 index 00000000..2ddf56c9 --- /dev/null +++ b/website/docs/library/access/Owner/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Owner", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/Owner/index" + } +} diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx new file mode 100644 index 00000000..9b04676f --- /dev/null +++ b/website/docs/library/access/Owner/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Owner" +description: "Single-owner access control pattern." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Single-owner access control pattern. + + +_No items in this category yet._ diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..034d1604 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -0,0 +1,286 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Manages ownership transfer with a two-step verification process." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ownership transfer with a two-step verification process. + + + +- Two-step ownership transfer for enhanced security. +- Provides view functions for `owner()` and `pendingOwner()`. +- Supports `renounceOwnership()` to remove owner privileges. + + +## Overview + +This facet implements a secure, two-step ownership transfer mechanism for Compose diamonds. It separates the initiation and finalization of ownership changes, preventing accidental or malicious takeovers. The facet provides read access to current and pending ownership states and allows for renouncing ownership. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoStepsFacet} from "@compose-protocol/diamond-contracts/facets/OwnerTwoSteps/IOwnerTwoStepsFacet.sol"; + +contract OwnerConsumer { + IOwnerTwoStepsFacet public ownerFacet; + + constructor(address _ownerFacetAddress) { + ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); + } + + function initiateOwnershipTransfer(address _newOwner) public { + ownerFacet.transferOwnership(_newOwner); + } + + function acceptNewOwnership() public { + ownerFacet.acceptOwnership(); + } + + function getCurrentOwner() public view returns (address) { + return ownerFacet.owner(); + } +}`} + + +## Best Practices + + +- Initialize ownership transfer using `transferOwnership(newOwner)`. +- The new owner must call `acceptOwnership()` to finalize the transfer. +- Use `renounceOwnership()` to permanently relinquish ownership. + + +## Security Considerations + + +Access to `transferOwnership` is restricted to the current owner. `acceptOwnership` is restricted to the pending owner. `renounceOwnership` is restricted to the current owner. Ensure the `OwnerTwoStepsFacet` is correctly initialized with the initial owner. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..fc647417 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -0,0 +1,297 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "Two-step contract ownership transfer for facets." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Two-step contract ownership transfer for facets. + + + +- Secure two-step ownership transfer process. +- Explicit owner acceptance mechanism. +- Capability to renounce ownership. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module implements a secure, two-step ownership transfer mechanism. It prevents accidental ownership loss by requiring explicit acceptance from the new owner. This pattern is crucial for managing administrative privileges in composable diamond architectures. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { +address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { +address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerTwoSteps} from "../interfaces/IOwnerTwoSteps.sol"; + +contract MyOwnerFacet { + address owner(); + function transferOwnership(address _newOwner) external { + // Call the module's transferOwnership function + IOwnerTwoSteps(msg.sender).transferOwnership(_newOwner); + } + + function acceptOwnership() external { + // Call the module's acceptOwnership function + IOwnerTwoSteps(msg.sender).acceptOwnership(); + } + + function getOwner() public view returns (address) { + return owner(); + } + + function renounceOwnership() external { + IOwnerTwoSteps(msg.sender).renounceOwnership(); + } +}`} + + +## Best Practices + + +- Use `transferOwnership` to initiate a transfer and require the new owner to call `acceptOwnership` to finalize it. +- Implement `requireOwner` checks judiciously to protect critical administrative functions. +- Be aware that `renounceOwnership` permanently removes administrative control. + + +## Integration Notes + + +The `OwnerTwoStepsMod` manages ownership state within specific storage slots. Facets that utilize this module should ensure they do not conflict with these storage positions. The `owner()`, `pendingOwner()`, `getOwnerStorage()`, and `getPendingOwnerStorage()` functions provide access to this state. Ownership checks are performed via the `requireOwner()` internal function. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json new file mode 100644 index 00000000..90b66a92 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Two-Step Owner", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/OwnerTwoSteps/index" + } +} diff --git a/website/docs/library/access/OwnerTwoSteps/index.mdx b/website/docs/library/access/OwnerTwoSteps/index.mdx new file mode 100644 index 00000000..525a9934 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Two-Step Owner" +description: "Two-step ownership transfer pattern." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Two-step ownership transfer pattern. + + +_No items in this category yet._ diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json new file mode 100644 index 00000000..cbc9d5ba --- /dev/null +++ b/website/docs/library/access/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/index" + } +} diff --git a/website/docs/library/access/index.mdx b/website/docs/library/access/index.mdx new file mode 100644 index 00000000..28850d68 --- /dev/null +++ b/website/docs/library/access/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Access Control" +description: "Access control patterns for permission management in Compose diamonds." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Access control patterns for permission management in Compose diamonds. + + +_No items in this category yet._ diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx new file mode 100644 index 00000000..d0dd0b4e --- /dev/null +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -0,0 +1,420 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Manage diamond facet additions, replacements, and removals." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facet additions, replacements, and removals. + + + +- Supports adding, replacing, and removing facets dynamically. +- Allows batching multiple facet operations in a single transaction. +- Optionally executes an initialization function after the cut. + + +## Overview + +The DiamondCutFacet enables the dynamic modification of a diamond's functionality by allowing the addition, replacement, or removal of facets. It provides a standardized interface for upgrading and extending the diamond's capabilities. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getOwnerStorage + +Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getDiamondStorage + + +{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/contracts/src/interfaces/IDiamondCut.sol"; + +contract Deployer { + address public diamond; + + function deployDiamond() public { + // ... deployment logic for diamond proxy and initial facets ... + diamond = address(0xYourDiamondAddress); + } + + function upgradeDiamond() public { + IDiamondCut diamondCutFacet = IDiamondCut(diamond); + + // Example: Add a new facet + address newFacetAddress = address(0xNewFacetAddress); + bytes4[] memory selectorsToAdd = new bytes4[](2); + selectorsToAdd[0] = bytes4(keccak256(bytes('newFunction1()'))); + selectorsToAdd[1] = bytes4(keccak256(bytes('newFunction2()'))); + + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: newFacetAddress, + action: IDiamondCut.Action.ADD, + selectors: selectorsToAdd + }); + + diamondCutFacet.diamondCut(cuts, address(0), ""); + } +}`} + + +## Best Practices + + +- Ensure the `diamondCut` function is called by an authorized address, typically the diamond owner or an admin contract. +- Carefully manage facet addresses and their associated selectors to avoid conflicts or unintended overwrites. +- When replacing facets, ensure the new facet's functions are compatible with existing diamond logic to maintain state integrity. + + +## Security Considerations + + +The `diamondCut` function is highly sensitive and should only be callable by authorized entities. Incorrectly adding, replacing, or removing facets can lead to loss of functionality or unintended state changes. Ensure proper access control is implemented. Be cautious when replacing immutable functions or functions that have already been deployed. Reentrancy is not a concern as the function performs state changes before delegatecalls. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx new file mode 100644 index 00000000..194401aa --- /dev/null +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -0,0 +1,401 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Manage diamond facets and functions on-chain." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and functions on-chain. + + + +- Supports adding, replacing, and removing functions from diamond facets atomically. +- Allows optional execution of an initialization function via delegatecall after the diamond cut. +- Provides error handling for common issues like non-existent selectors or immutable function modifications. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCutMod enables dynamic on-chain management of diamond facets. It allows adding, replacing, and removing functions, providing a flexible upgrade path for Compose diamonds. This module is critical for evolving diamond functionality without redeploying the entire proxy. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * Array of all function selectors that can be called in the diamond + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond/contracts/modules/diamondCut/IDiamondCut.sol"; +import {FacetCut} from "@compose/diamond/contracts/modules/diamondCut/IDiamondCut.sol"; + +contract MyFacet { + // ... other facet functions ... + + function upgradeDiamond(address _diamondCutAddress, FacetCut[] memory _diamondCut) public { + IDiamondCut(_diamondCutAddress).diamondCut(_diamondCut, address(0), ""); + } + + // Example of adding a new facet + function addMyNewFacet(address _diamondCutAddress, address _newFacetAddress, bytes4[] memory _selectors) public { + FacetCut[] memory cuts = new FacetCut[](1); + cuts[0] = FacetCut({ + facetAddress: _newFacetAddress, + action: 1, // 1 = ADD + selectors: _selectors + }); + IDiamondCut(_diamondCutAddress).diamondCut(cuts, address(0), ""); + } + + // Example of replacing a facet's function + function replaceMyFunction(address _diamondCutAddress, address _facetAddress, bytes4 _selector, address _newFacetAddress) public { + FacetCut[] memory cuts = new FacetCut[](1); + cuts[0] = FacetCut({ + facetAddress: _newFacetAddress, + action: 2, // 2 = REPLACE + selectors: new bytes4[](1) { _selector } + }); + IDiamondCut(_diamondCutAddress).diamondCut(cuts, address(0), ""); + } + + // Example of removing a facet's function + function removeMyFunction(address _diamondCutAddress, address _facetAddress, bytes4 _selector) public { + FacetCut[] memory cuts = new FacetCut[](1); + cuts[0] = FacetCut({ + facetAddress: address(0), // Address must be zero for removal + action: 3, // 3 = REMOVE + selectors: new bytes4[](1) { _selector } + }); + IDiamondCut(_diamondCutAddress).diamondCut(cuts, address(0), ""); + } +} +`} + + +## Best Practices + + +- Ensure the `diamondCut` function is called with appropriate access control, typically restricted to an owner or admin role. +- Carefully validate `facetAddress` and `selectors` to prevent accidental removal of critical functions or addition of malicious ones. +- Be aware of immutable functions; they cannot be removed or replaced. Use the `getStorage` function to inspect facet information if needed. + + +## Integration Notes + + +The DiamondCutMod interacts with the diamond's global function selector mapping. When functions are added, replaced, or removed, the DiamondCutMod updates this mapping. Facets interact with the diamond proxy, which routes calls based on this mapping. Immutable functions, identified during the cut, are permanently registered and cannot be altered via `diamondCut`. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..ffcac34f --- /dev/null +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -0,0 +1,252 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "Inspect diamond facets, selectors, and storage." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Inspect diamond facets, selectors, and storage. + + + +- Provides full visibility into diamond's facet and selector configuration. +- Optimized for gas efficiency when querying large numbers of selectors and facets. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are registered, the function selectors they handle, and the addresses of these facets, facilitating debugging and external tooling integration. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### getStorage + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +--- +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupe} from "@compose/diamond/facets/DiamondLoupe/IDiamondLoupe.sol"; + +contract ExampleUsage { + IDiamondLoupe public diamondLoupe; + + constructor(address _diamondAddress) { + diamondLoupe = IDiamondLoupe(_diamondAddress); + } + + function getAllFacets() public view returns (IDiamondLoupe.Facet[] memory) { + return diamondLoupe.facets(); + } + + function getFacetAddress(bytes4 _selector) public view returns (address) { + return diamondLoupe.facetAddress(_selector); + } + + function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { + return diamondLoupe.facetFunctionSelectors(_facet); + } +}`} + + +## Best Practices + + +- Integrate DiamondLoupeFacet into your diamond to enable runtime inspection of its components. +- Use the provided functions to verify facet registration and selector mappings during development and upgrades. + + +## Security Considerations + + +This facet is read-only and does not pose direct security risks. However, relying on its output for critical access control logic in external contracts could be problematic if the diamond's state changes unexpectedly. Always ensure contracts interacting with the diamond's functions have appropriate authorization checks. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx new file mode 100644 index 00000000..b22777a1 --- /dev/null +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -0,0 +1,243 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Manages diamond facets and provides core proxy logic." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond facets and provides core proxy logic. + + + +- Supports adding multiple facets and their function selectors during initial diamond deployment. +- Acts as the diamond's central fallback for routing external calls to the correct facet implementation. +- Provides a mechanism (`getStorage`) to retrieve the facet address associated with a given function selector. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides essential functions for managing facets within a Compose diamond. It handles the addition of new facets during initial deployment and acts as the central fallback mechanism to route external calls to the appropriate facet implementation. Understanding its storage interactions is key for facet composition. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { +mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; +/** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ +bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { +address facet; +uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { +address facetAddress; +FacetCutAction action; +bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondMod} from "@compose/diamond-proxy/contracts/modules/DiamondMod.sol"; + +contract MyFacet { + IDiamondMod public diamondMod; + + function initialize(address _diamondMod) external { + diamondMod = IDiamondMod(_diamondMod); + } + + /** + * @notice Calls a function on another facet via the DiamondMod. + */ + function callOtherFacet(bytes4 _functionSelector, address _facetAddress, bytes memory _calldata) external returns (bytes memory) { + // This is a simplified example. In a real scenario, the facet address would be dynamically resolved. + // The actual diamondFallback would handle this resolution internally. + return diamondMod.diamondFallback(_functionSelector, _facetAddress, _calldata); + } + + /** + * @notice Gets the storage slot for a specific function selector. + */ + function getFunctionStorage(bytes4 _functionSelector) external view returns (address) { + return diamondMod.getStorage(_functionSelector); + } +}`} + + +## Best Practices + + +- Facet functions should only be added during the initial diamond deployment phase via `addFacets`. Calls to `addFacets` after deployment are unsupported and will revert. +- Handle `FunctionNotFound` errors gracefully, as they indicate an attempt to call a non-existent function selector. +- Utilize `getStorage` to understand the storage layout and potential interactions between facets. + + +## Integration Notes + + +The `DiamondMod` interacts directly with the diamond's storage to map function selectors to facet addresses. The `addFacets` function is designed to be called only once during deployment. The `diamondFallback` function is the core of the diamond proxy pattern, resolving function calls by looking up the selector in storage and then executing the corresponding facet. The `getStorage` function exposes this mapping for external inspection. + + +
+ +
+ + diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json new file mode 100644 index 00000000..26c8cc37 --- /dev/null +++ b/website/docs/library/diamond/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Diamond Core", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/diamond/index" + } +} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx new file mode 100644 index 00000000..ea84010d --- /dev/null +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -0,0 +1,130 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Example Diamond for Compose framework" +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Example Diamond for Compose framework + + + +- Manages facet registration and function selector mapping. +- Supports diamond cut operations for dynamic facet updates. +- Acts as a foundational example for Compose diamond deployments. + + +## Overview + +The ExampleDiamond serves as a foundational contract within the Compose framework, demonstrating diamond proxy functionality. It manages facet registrations and routes calls to appropriate implementation contracts, showcasing the core composability principles. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ExampleDiamond} from "@compose-protocol/diamond/contracts/ExampleDiamond.sol"; + +contract DeployExampleDiamond { + ExampleDiamond public diamond; + + function deploy() public { + // Assuming facet addresses and selectors are defined elsewhere + // Example: address facetAddress = address(new MyFacet()); + // Example: bytes4[] memory selectors = new bytes4[](1); + // selectors[0] = MyFacet.myFunction.selector; + // FacetCut[] memory facets = new FacetCut[](1); + // facets[0] = ExampleDiamond.FacetCut({ + // facetAddress: facetAddress, + // action: ExampleDiamond.FacetCutAction.Add, + // functionSelectors: selectors + // }); + + // In a real scenario, you would pass actual facet data + diamond = new ExampleDiamond(); + // diamond.diamondCut(facets, address(0), ""); // Example cut call + } +}`} + + +## Best Practices + + +- Use the `constructor` to initialize the diamond with initial facets and set the owner. +- Leverage `diamondCut` for adding, replacing, or removing facets post-deployment. +- Ensure facet addresses and their associated function selectors are correctly managed during cuts. + + +## Security Considerations + + +Access control for diamond cut operations should be strictly managed to prevent unauthorized facet modifications. Ensure that facet addresses provided during initialization or cuts are trusted implementations. + + +
+ +
+ + diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json new file mode 100644 index 00000000..8e4d0ed5 --- /dev/null +++ b/website/docs/library/diamond/example/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "example", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/diamond/example/index" + } +} diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx new file mode 100644 index 00000000..8d62a9ae --- /dev/null +++ b/website/docs/library/diamond/example/index.mdx @@ -0,0 +1,14 @@ +--- +title: "example" +description: "example components for Compose diamonds." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + example components for Compose diamonds. + + +_No items in this category yet._ diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx new file mode 100644 index 00000000..b11c7a6f --- /dev/null +++ b/website/docs/library/diamond/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Diamond Core" +description: "Core diamond proxy functionality for ERC-2535 diamonds." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Core diamond proxy functionality for ERC-2535 diamonds. + + +_No items in this category yet._ diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx new file mode 100644 index 00000000..4f169ade --- /dev/null +++ b/website/docs/library/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Library" +description: "API reference for all Compose modules and facets." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + API reference for all Compose modules and facets. + + +_No items in this category yet._ diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx new file mode 100644 index 00000000..11cc600b --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -0,0 +1,152 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "ERC-165 interface detection and registration." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-165 interface detection and registration. + + + +- Standardized ERC-165 interface detection. +- Allows facets to declare supported interfaces at runtime. +- Utilizes a dedicated storage slot for interface support. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod provides a standardized way for facets to implement and report ERC-165 interface support. By registering interfaces during facet initialization, other contracts can reliably query for supported functionality through the diamond proxy. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { +/* + * @notice Mapping of interface IDs to whether they are supported + */ +mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC165} from "@compose/modules/erc165/LibERC165.sol"; +import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; + +contract MyERC721Facet { + LibERC165.ERC165Storage internal _erc165Storage; + + function initialize(address _diamondProxy) external { + // Register ERC721 interface support + LibERC165.registerInterface(address(this), type(IERC721).interfaceId); + } + + // Other ERC721 facet functions... +}`} + + +## Best Practices + + +- Register all supported interfaces during facet initialization. +- Ensure the `ERC165Storage` struct is correctly included in the diamond's storage layout. +- Avoid calling `registerInterface` for interfaces not actually implemented by the facet. + + +## Integration Notes + + +The ERC165Mod relies on a dedicated storage slot for its `ERC165Storage` struct. Facets that implement ERC-165 must include this struct in their own storage layout and call `LibERC165.registerInterface` during their initialization. The `getStorage` function uses inline assembly to bind to the correct storage position, ensuring interface detection works correctly through the diamond proxy. + + +
+ +
+ + diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json new file mode 100644 index 00000000..2396f18a --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-165", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/interfaceDetection/ERC165/index" + } +} diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx new file mode 100644 index 00000000..fcd9a680 --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-165" +description: "ERC-165 components for Compose diamonds." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-165 components for Compose diamonds. + + +_No items in this category yet._ diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json new file mode 100644 index 00000000..a184d836 --- /dev/null +++ b/website/docs/library/interfaceDetection/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Interface Detection", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/interfaceDetection/index" + } +} diff --git a/website/docs/library/interfaceDetection/index.mdx b/website/docs/library/interfaceDetection/index.mdx new file mode 100644 index 00000000..0e77e722 --- /dev/null +++ b/website/docs/library/interfaceDetection/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Interface Detection" +description: "ERC-165 interface detection support." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-165 interface detection support. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx new file mode 100644 index 00000000..7131c540 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -0,0 +1,674 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "Manages ERC-1155 fungible and non-fungible tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 fungible and non-fungible tokens. + + + +- Supports both fungible and non-fungible token types within a single contract. +- Implements batched transfer and balance checking for efficiency. +- Handles operator approvals for delegated token management. +- Provides flexible URI resolution for token metadata. + + +## Overview + +The ERC1155Facet provides a comprehensive implementation of the ERC-1155 Multi-Token Standard. It enables a Compose diamond to manage multiple token types, supporting both fungible and non-fungible assets. This facet handles token transfers, batch operations, approvals, and URI resolution, making it a core component for any diamond requiring flexible token management. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Errors, IERC1155Facet} from "@compose/diamond/facets/ERC1155/IERC1155Facet.sol"; +import {DiamondProxy, IDiamondProxy} from "@compose/diamond/DiamondProxy.sol"; + +contract ERC1155Consumer { + // Assume diamondProxy is already deployed and initialized + IDiamondProxy public diamondProxy; + + constructor(address _diamondProxyAddress) { + diamondProxy = IDiamondProxy(_diamondProxyAddress); + } + + // Example: Get balance of a specific token for an account + function getMyTokenBalance(uint256 _tokenId, address _account) external view returns (uint256) { + bytes4 selector = IERC1155Facet.balanceOf.selector; + (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector, _tokenId, _account)); + require(success, "ERC1155Consumer: balanceOf call failed"); + return abi.decode(data, (uint256)); + } + + // Example: Get the URI for a token + function getTokenUri(uint256 _tokenId) external view returns (string memory) { + bytes4 selector = IERC1155Facet.uri.selector; + (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector, _tokenId)); + require(success, "ERC1155Consumer: uri call failed"); + return abi.decode(data, (string)); + } +}`} + + +## Best Practices + + +- Ensure the ERC1155Facet is correctly initialized with appropriate base URIs or token-specific URIs if needed. +- Implement robust access control within your diamond's governance or access control facets to manage who can call administrative functions on ERC1155Facet, such as setting approvals. +- When designing new token types, consider the `uri` function's behavior regarding base URIs and token-specific URIs for optimal metadata management. + + +## Security Considerations + + +The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks to prevent unsafe transfers to non-contract addresses or contract addresses that do not implement the `onERC1155Received` or `onERC1155BatchReceived` hooks. Ensure that any contracts interacting with this facet are audited for reentrancy if they implement these hooks. Access control for `setApprovalForAll` should be managed carefully to prevent unauthorized token delegation. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx new file mode 100644 index 00000000..c82aeaa4 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -0,0 +1,604 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "Manages ERC-1155 token balances, transfers, and metadata." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 token balances, transfers, and metadata. + + + +- Supports both single and batch operations for minting, burning, and transferring tokens. +- Implements safe transfer mechanisms, including `IERC1155Receiver` callback validation. +- Provides functionality to set and retrieve base URIs and token-specific URIs for metadata. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod facet implements the core logic for managing ERC-1155 fungible and non-fungible tokens. It provides functionalities for minting, burning, transferring tokens safely, and setting token URIs. This module ensures proper balance tracking and adherence to ERC-1155 standards, enabling composable token management within a diamond. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { +mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; +mapping(address account => mapping(address operator => bool)) isApprovedForAll; +string uri; +string baseURI; +mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Mod} from "@compose/modules/erc1155/ERC1155Mod.sol"; + +contract MyDiamondFacet { + IERC1155Mod internal immutable erc1155Mod; + + constructor(address _diamondAddress) { + erc1155Mod = IERC1155Mod(_diamondAddress); + } + + /** + * @notice Mints 10 units of token ID 1 to address(1) and safely transfers 5 units of token ID 2 from msg.sender to address(1). + */ + function manageErc1155Tokens() external { + // Mint tokens + erc1155Mod.mint(address(1), 1, 10); + + // Safely transfer tokens + erc1155Mod.safeTransferFrom(msg.sender, address(1), 2, 5); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented for minting and burning functions if they are intended to be restricted. +- Always validate that the recipient contract implements the `IERC1155Receiver` interface when performing safe transfers to contracts. +- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, `ERC1155InvalidReceiver`, `ERC1155InvalidSender`, and `ERC1155MissingApprovalForAll` errors appropriately in your calling facets. + + +## Integration Notes + + +This module interacts with the diamond's storage to manage ERC-1155 token balances and URIs. The `getStorage` function provides direct access to the ERC-1155 storage struct, allowing other facets to read or modify state directly if necessary, though direct modification should be done with extreme caution to maintain invariants. The ERC-1155 storage struct is expected to be located at a predefined diamond storage slot. Any changes to this storage layout require careful consideration to maintain backward compatibility for existing facets. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json new file mode 100644 index 00000000..cdb57d9a --- /dev/null +++ b/website/docs/library/token/ERC1155/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-1155", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC1155/index" + } +} diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx new file mode 100644 index 00000000..445e1f0e --- /dev/null +++ b/website/docs/library/token/ERC1155/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-1155" +description: "ERC-1155 multi-token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-1155 multi-token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx new file mode 100644 index 00000000..1f477e0e --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -0,0 +1,248 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Burn ERC-20 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC-20 tokens within a Compose diamond. + + + +- Allows burning of ERC-20 tokens directly via the diamond proxy. +- Supports burning from the caller's balance (`burn`). +- Supports burning from an allowance granted by another account (`burnFrom`). + + +## Overview + +The ERC20BurnFacet enables the burning of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account's allowance, facilitating token destruction mechanisms. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BurnFacet} from "@compose/contracts/facets/erc20/IERC20BurnFacet.sol"; + +contract ERC20BurnConsumer { + // Replace with the actual diamond proxy address + address immutable DIAMOND_PROXY; + + constructor(address _diamondProxy) { + DIAMOND_PROXY = _diamondProxy; + } + + function burnMyTokens(address _tokenAddress, uint256 _amount) external { + IERC20BurnFacet(DIAMOND_PROXY).burn(_tokenAddress, _amount); + } + + function burnOtherTokens(address _tokenAddress, address _from, uint256 _amount) external { + IERC20BurnFacet(DIAMOND_PROXY).burnFrom(_tokenAddress, _from, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20BurnFacet is correctly added to the diamond proxy during deployment or upgrade. +- Use `burnFrom` only when the caller has a sufficient allowance for the specified token and amount. + + +## Security Considerations + + +The `burn` function relies on the caller's balance. The `burnFrom` function relies on the caller's allowance. Ensure sufficient balances and allowances are managed correctly to prevent `ERC20InsufficientBalance` or `ERC20InsufficientAllowance` errors. No reentrancy concerns as the facet does not make external calls. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx new file mode 100644 index 00000000..57e1097f --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -0,0 +1,573 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Standard ERC-20 token functionality for Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Standard ERC-20 token functionality for Compose diamonds. + + + +- Implements the full ERC-20 token standard interface. +- Manages token supply, balances, and allowances directly within the diamond's storage. +- Supports standard ERC-20 events (`Transfer`, `Approval`) for off-chain tracking. + + +## Overview + +This facet provides a complete implementation of the ERC-20 token standard, enabling tokens to be managed and interacted with within a Compose diamond. It exposes core functions for querying token metadata, managing balances, and handling approvals and transfers, ensuring interoperability with the broader Ethereum ecosystem. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20Facet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; + +contract ERC20Deployment { + address internal diamondAddress; + + function deploy(address _diamondAddress) external { + diamondAddress = _diamondAddress; + } + + function addERC20Facet(bytes4[] memory selectors) external { + // Assuming ERC20Facet is already deployed and its address is known + address erc20FacetAddress = address(0x1234567890123456789012345678901234567890); // Replace with actual ERC20Facet address + + IDiamondCut(diamondAddress).diamondCut([ + IDiamondCut.FacetCut({ + facetAddress: erc20FacetAddress, + action: IDiamondCut.FacetCutAction.ADD, + selectors: selectors // e.g., ERC20Facet.name.selector, ERC20Facet.symbol.selector, etc. + }) + ], address(0), ""); + } + + function transferTokens(address _to, uint256 _amount) external { + IERC20Facet(diamondAddress).transfer(_to, _amount); + } +} +`} + + +## Best Practices + + +- Initialize token metadata (name, symbol, decimals) during diamond deployment, typically within a separate initialization facet or a dedicated deployment script. +- Ensure the `approve` function is used correctly to grant spending allowances before invoking `transferFrom` to prevent unintended token movements. +- Leverage diamond upgradeability to replace or add ERC-20 functionality without altering the diamond's core address. + + +## Security Considerations + + +Input validation is crucial for all transfer and approval functions to prevent integer overflows and underflows. Ensure sender and receiver addresses are valid. Allowance checks must be strictly enforced in `transferFrom` to prevent unauthorized spending. Reentrancy is not a concern as state changes occur before external calls (which are not present in this facet). + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx new file mode 100644 index 00000000..ffd15274 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -0,0 +1,422 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "ERC-20 token logic with mint, burn, transfer, and approval functions." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-20 token logic with mint, burn, transfer, and approval functions. + + + +- Supports standard ERC-20 operations: mint, burn, transfer, and approve. +- Defines a fixed storage slot for ERC-20 state, promoting composability. +- Utilizes custom errors for clear and gas-efficient error reporting. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod provides essential ERC-20 token functionalities including minting, burning, transfers, and approvals. It defines a standard storage layout for ERC-20 state, allowing facets to compose this logic into a diamond proxy. This enables a single diamond to manage multiple ERC-20 tokens or integrate ERC-20 functionality alongside other features. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +string symbol; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "./IERC20Mod.sol"; + +contract MyTokenFacet { + IERC20Mod private immutable _erc20Mod; + + constructor(address _diamondProxy) { + _erc20Mod = IERC20Mod(_diamondProxy); + } + + function mintTokens(address _to, uint256 _amount) external { + _erc20Mod.mint(_to, _amount); + } + + function transferTokens(address _to, uint256 _amount) external { + _erc20Mod.transfer(msg.sender, _to, _amount); + } + + function approveSpender(address _spender, uint256 _amount) external { + _erc20Mod.approve(msg.sender, _spender, _amount); + } +}`} + + +## Best Practices + + +- Ensure the ERC20Mod facet is correctly initialized with the diamond proxy address. +- Handle `ERC20InsufficientAllowance`, `ERC20InsufficientBalance`, and `ERC20InvalidReceiver` errors to manage token operations robustly. +- When upgrading or modifying facets, be aware of the ERC-20 storage layout to maintain data integrity. + + +## Integration Notes + + +The ERC20Mod utilizes a specific storage slot to hold its `ERC20Storage` struct. Facets interacting with this module should call `getStorage()` using inline assembly to obtain a pointer to this struct, ensuring correct access to balances, allowances, and total supply. Any changes to the `ERC20Storage` struct, such as adding new trailing variables, must be carefully managed to avoid breaking existing facets. The order of variables within the struct must be preserved. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json new file mode 100644 index 00000000..bd8d3da5 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/ERC20/index" + } +} diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx new file mode 100644 index 00000000..f7559880 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-20" +description: "ERC-20 fungible token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 fungible token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..ecd4687b --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -0,0 +1,434 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "Facilitates cross-chain token bridging for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Facilitates cross-chain token bridging for ERC-20 tokens. + + + +- Enables cross-chain minting and burning of ERC-20 tokens. +- Enforces `trusted-bridge` role for all cross-chain operations. +- Provides internal access control checks for bridge trustworthiness. + + +## Overview + +The ERC20BridgeableFacet enables secure and controlled cross-chain minting and burning operations for ERC-20 tokens within a Compose diamond. It ensures that only trusted bridge addresses can initiate these operations, maintaining the integrity of token balances across different networks. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### getAccessControlStorage + + +{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "../facets/ERC20BridgeableFacet.sol"; +import {IDiamondCut} from "../diamond/interfaces/IDiamondCut.sol"; + +contract DeployERC20BridgeableFacet { + address constant ERC20_BRIDGEABLE_FACET_IMPL = address(0xYourERC20BridgeableFacetImplementationAddress); + + // Function to add the ERC20BridgeableFacet to a diamond + function addERC20BridgeableFacet(address _diamondAddress) external { + bytes[] memory selectors = new bytes[](5); + selectors[0] = IERC20BridgeableFacet.getERC20Storage.selector; + selectors[1] = IERC20BridgeableFacet.getAccessControlStorage.selector; + selectors[2] = IERC20BridgeableFacet.crosschainMint.selector; + selectors[3] = IERC20BridgeableFacet.crosschainBurn.selector; + selectors[4] = IERC20BridgeableFacet.checkTokenBridge.selector; + + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: ERC20_BRIDGEABLE_FACET_IMPL, + action: IDiamondCut.FacetCutAction.Add, + selectors: selectors + }); + + // Assume _diamondAddress is the address of your diamond proxy + // The diamond cut function is typically called on the diamond itself + // For example: IDiamondCut(_diamondAddress).diamondCut(cut, address(0), ""); + } + + // Example of calling crosschainMint (requires trusted-bridge role) + function mintForBridge(address _diamondAddress, address _to, uint256 _amount) external { + // Assume _diamondAddress is the address of your diamond proxy + IERC20BridgeableFacet(_diamondAddress).crosschainMint(_to, _amount); + } + + // Example of calling crosschainBurn (requires trusted-bridge role) + function burnForBridge(address _diamondAddress, address _from, uint256 _amount) external { + // Assume _diamondAddress is the address of your diamondส์ proxy + IERC20BridgeableFacet(_diamondAddress).crosschainBurn(_from, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `trusted-bridge` role is assigned only to verified and secure bridge addresses to prevent unauthorized cross-chain operations. +- Utilize the `checkTokenBridge` internal function or its logic within external calls to enforce access control before executing mint or burn operations. +- Store and manage diamond storage slots correctly when integrating this facet to avoid state corruption. + + +## Security Considerations + + +Access to `crosschainMint` and `crosschainBurn` is restricted to addresses with the `trusted-bridge` role. The `checkTokenBridge` function verifies this role, preventing unauthorized cross-chain token transfers. Ensure the role management system for `trusted-bridge` is robust and secure to prevent malicious actors from gaining control. Reentrancy is not a direct concern for mint/burn functions themselves, but dependent external calls should be carefully audited. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..71eccdae --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -0,0 +1,437 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "Manage cross-chain ERC20 token transfers and burns." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage cross-chain ERC20 token transfers and burns. + + + +- Enables cross-chain minting and burning of ERC20 tokens. +- Enforces strict access control via a `trusted-bridge` role. +- Provides utility functions to retrieve module storage pointers. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Bridgeable module provides functionality for minting and burning ERC20 tokens across different chains. It enforces access control to ensure only trusted bridge operators can perform these sensitive operations, enhancing security for cross-chain token management. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { +mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +}`} + + +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableMod} from "../modules/ERC20BridgeableMod.sol"; +import {DiamondStorage} from "../facets/DiamondStorage.sol"; + +contract ERC20BridgeableFacet { + using DiamondStorage for DiamondStorage; + + IERC20BridgeableMod private _erc20BridgeableMod; + + function setERC20BridgeableMod(address _addr) external { + _erc20BridgeableMod = IERC20BridgeableMod(_addr); + } + + /** + * @notice Mints ERC20 tokens on a different chain. + * @dev Requires the caller to have the 'trusted-bridge' role. + * @param _to The recipient of the minted tokens. + * @param _amount The amount of tokens to mint. + */ + function crosschainMint(address _to, uint256 _amount) external { + // Assume access control is handled by the trusted bridge mechanism + _erc20BridgeableMod.crosschainMint(_to, _amount); + } + + /** + * @notice Burns ERC20 tokens on a different chain. + * @dev Requires the caller to have the 'trusted-bridge' role. + * @param _from The sender of the tokens to burn. + * @param _amount The amount of tokens to burn. + */ + function crosschainBurn(address _from, uint256 _amount) external { + // Assume access control is handled by the trusted bridge mechanism + _erc20BridgeableMod.crosschainBurn(_from, _amount); + } +}`} + + +## Best Practices + + +- Only addresses with the `trusted-bridge` role can call `crosschainMint` and `crosschainBurn`. +- Ensure the `AccessControl` facet is properly configured with trusted bridge addresses before deploying this module. +- Handle `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, and `ERC20InvalidReciever` errors appropriately in your facet logic. + + +## Integration Notes + + +This module interacts with the ERC20 storage slot defined by the diamond proxy. The `getERC20Storage` function provides direct access to this storage. The `checkTokenBridge` internal function enforces trust for cross-chain operations, relying on the `trusted-bridge` role within the `AccessControl` facet. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json new file mode 100644 index 00000000..03768f44 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Bridgeable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/ERC20Bridgeable/index" + } +} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx new file mode 100644 index 00000000..72f662a2 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-20 Bridgeable" +description: "ERC-20 Bridgeable extension for ERC-20 tokens." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 Bridgeable extension for ERC-20 tokens. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx new file mode 100644 index 00000000..644fb0c4 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -0,0 +1,340 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "Handles EIP-2612 permit functionality for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Handles EIP-2612 permit functionality for ERC-20 tokens. + + + +- Implements EIP-2612 permit functionality. +- Enables gas-less token approvals via signed messages. +- Provides `nonces` for replay protection. +- Exposes `DOMAIN_SEPARATOR` for signature verification. + + +## Overview + +The ERC20PermitFacet enables gas-less approvals for ERC-20 tokens by allowing users to sign permit messages off-chain. This facet integrates EIP-2612 permit functionality, reducing transaction costs for users and improving the user experience for token transfers and approvals. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +### State Variables + + + +## Functions + +### getERC20Storage + + +{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} + + +--- +### getStorage + + +{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} + + +--- +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import {DiamondLoupeFacet} from "@compose-protocol/diamond-governance/facets/DiamondLoupe/DiamondLoupeFacet.sol"; + +contract MyDiamond is DiamondLoupeFacet { + // ... other facets + address public constant ERC20_PERMIT_FACET_ADDRESS = address(0x...); // Replace with actual facet address + + function permitERC20(address token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Delegate call to the ERC20PermitFacet + (bool success, ) = ERC20_PERMIT_FACET_ADDRESS.call(abi.encodeWithSelector( + ERC20PermitFacet.permit.selector, + token, + owner, + spender, + value, + deadline, + v, + r, + s + )); + require(success, "ERC20PermitFacet: permit call failed"); + } + + // Example of calling permit via a helper function + function approveToken(address token, address spender, uint256 amount) external { + // Obtain permit details off-chain and pass them here + // ... obtain v, r, s, deadline, owner, value + permitERC20(token, msg.sender, spender, amount, /* deadline */, /* v */, /* r */, /* s */); + } +}`} + + +## Best Practices + + +- Integrate this facet to enable gas-less token approvals for your ERC-20 tokens. +- Ensure the `DOMAIN_SEPARATOR` is correctly computed and used in signature generation to prevent cross-chain replay attacks. +- Store the `nonces` mapping securely within the diamond's storage. + + +## Security Considerations + + +The `permit` function relies on off-chain signed messages. Ensure that the signature verification logic is robust and that the `owner`'s private key is securely managed. The `nonces` mapping must be updated atomically with the allowance change to prevent replay attacks. Reentrancy is not a direct concern for the `permit` function itself, but downstream token transfers should be audited. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx new file mode 100644 index 00000000..5c7081a6 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -0,0 +1,277 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "Enables ERC-2612 permit functionality for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enables ERC-2612 permit functionality for ERC-20 tokens. + + + +- Implements ERC-2612 Permit standard for gasless approvals. +- Generates and validates EIP-712 domain separators for secure signing. +- Provides a dedicated storage slot for permit-related state. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the necessary logic and storage for implementing the ERC-2612 Permit standard. It allows users to grant token allowances via signed messages, enhancing user experience by reducing gas costs for frequent transfers. The module ensures secure domain separation for signature validation. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { +mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { +mapping(address owner => uint256 balance) balanceOf; +uint256 totalSupply; +mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +uint8 decimals; +string name; +}`} + + +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibERC20Permit} from "@compose/modules/erc20/ERC20Permit.sol"; +import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; + +contract MyERC20Facet { + using LibERC20Permit for LibERC20Permit.ERC20PermitStorage; + + LibERC20Permit.ERC20PermitStorage internal _permitStorage; + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Retrieve the diamond storage for the ERC20Permit module + LibERC20Permit.ERC20PermitStorage storage permitStorage = LibERC20Permit.getPermitStorage(LibERC20Permit.getERC20Storage()); + + // Call the permit function from the library + permitStorage.permit(owner, spender, value, deadline, v, r, s); + } + + // Other ERC20 functions like allowance, approve, transferFrom... +}`} + + +## Best Practices + + +- Ensure the calling facet correctly implements the `Approval` event emission after a successful `permit` call. +- Validate the `deadline` parameter to prevent stale permit approvals. +- Use the `DOMAIN_SEPARATOR` provided by the module for accurate signature generation by off-chain signers. + + +## Integration Notes + + +The `LibERC20Permit` module manages its own dedicated storage, which is accessed via `LibERC20Permit.getPermitStorage(LibERC20Permit.getERC20Storage())`. Facets interacting with this module must ensure they have the correct storage layout and call the `permit` function, which will internally handle signature validation and allowance updates. The `Approval` event must be emitted by the calling facet after the library function successfully executes. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json new file mode 100644 index 00000000..7932c4df --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Permit", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/ERC20Permit/index" + } +} diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx new file mode 100644 index 00000000..25d1aee6 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-20 Permit" +description: "ERC-20 Permit extension for ERC-20 tokens." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 Permit extension for ERC-20 tokens. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json new file mode 100644 index 00000000..0e078cb1 --- /dev/null +++ b/website/docs/library/token/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/index" + } +} diff --git a/website/docs/library/token/ERC20/index.mdx b/website/docs/library/token/ERC20/index.mdx new file mode 100644 index 00000000..f7559880 --- /dev/null +++ b/website/docs/library/token/ERC20/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-20" +description: "ERC-20 fungible token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 fungible token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx new file mode 100644 index 00000000..c0177e63 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -0,0 +1,526 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "Manages ERC-6909 token balances and operator permissions." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-6909 token balances and operator permissions. + + + +- Implements ERC-6909 standard for token management. +- Supports both fungible and non-fungible token IDs. +- Enables granular approval and operator delegation mechanisms. +- Provides essential query functions like `balanceOf` and `allowance`. + + +## Overview + +The ERC6909Facet implements the ERC-6909 standard, enabling fungible and non-fungible token management within a Compose diamond. It handles balance tracking, approvals, and operator roles, providing a standardized interface for token operations. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Facet} from "@compose/diamond/facets/ERC6909/IERC6909Facet.sol"; +import {IDiamondCut} from "@compose/diamond/core/IDiamondCut.sol"; + +contract ERC6909User { + address constant DIAMOND_ADDRESS = address(0x1234567890abcdef); // Replace with your diamond address + + function getUserBalance(uint256 _id) external view returns (uint256) { + // Calldata for ERC6909Facet's balanceOf function + bytes4 selector = IERC6909Facet.balanceOf.selector; + (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _id)); + require(success, "balanceOf call failed"); + return abi.decode(data, (uint256)); + } + + function approveToken(uint256 _id, address _spender, uint256 _amount) external { + // Calldata for ERC6909Facet's approve function + bytes4 selector = IERC6909Facet.approve.selector; + (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _id, _spender, _amount)); + require(success, "approve call failed"); + } +}`} + + +## Best Practices + + +- Initialize operator roles and approvals carefully, as they grant significant permissions. +- When upgrading, ensure the storage layout of the ERC6909 facet is compatible to prevent data loss or corruption. +- Use `setOperator` to manage operator relationships efficiently, rather than repeated `approve` calls for general access. + + +## Security Considerations + + +Ensure proper access control is implemented at the diamond level for functions that modify state (e.g., `transfer`, `transferFrom`, `approve`, `setOperator`). Input validation for token IDs, amounts, senders, and receivers is handled by the facet's error conditions. Be mindful of reentrancy if interacting with external contracts in your diamond's logic that calls this facet. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx new file mode 100644 index 00000000..3760b355 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -0,0 +1,517 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "Manages ERC-6909 token logic including minting, burning, and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-6909 token logic including minting, burning, and transfers. + + + +- Supports standard ERC-6909 token operations: mint, burn, transfer, approve, setOperator. +- Utilizes inline assembly for efficient storage access via `getStorage`. +- Defines custom errors for specific failure conditions, enhancing gas efficiency and clarity. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC6909Mod module provides the core internal logic and storage for implementing the ERC-6909 standard within a Compose diamond. It enables efficient management of multi-token fungible assets, supporting standard operations like approvals, transfers, minting, and burning. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { +mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; +mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; +mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod} from "./IERC6909Mod.sol"; + +contract ERC6909Facet { + IERC6909Mod public immutable erc6909Mod; + + constructor(address _diamondAddress) { + erc6909Mod = IERC6909Mod(_diamondAddress); + } + + function mint(uint256 _id, uint256 _amount, address _to) external { + erc6909Mod.mint(_id, _amount, _to); + } + + function transfer(uint256 _id, uint256 _amount, address _from, address _to) external { + erc6909Mod.transfer(_id, _amount, _from, _to); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented in facets calling module functions. +- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` gracefully. +- Be mindful of potential reentrancy if facets implementing ERC6909 logic interact with external contracts. + + +## Integration Notes + + +This module relies on a specific storage slot defined by `STORAGE_POSITION` for its internal state. Facets interacting with this module must ensure that this storage slot is correctly allocated and managed within the diamond's storage layout. The `getStorage` function provides a direct pointer to the `ERC6909Storage` struct, allowing facets to read and write token balances and allowances. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json new file mode 100644 index 00000000..d4d084dc --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC6909/ERC6909/index" + } +} diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx new file mode 100644 index 00000000..f146bbc5 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-6909" +description: "ERC-6909 minimal multi-token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-6909 minimal multi-token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json new file mode 100644 index 00000000..42f1101f --- /dev/null +++ b/website/docs/library/token/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC6909/index" + } +} diff --git a/website/docs/library/token/ERC6909/index.mdx b/website/docs/library/token/ERC6909/index.mdx new file mode 100644 index 00000000..f146bbc5 --- /dev/null +++ b/website/docs/library/token/ERC6909/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-6909" +description: "ERC-6909 minimal multi-token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-6909 minimal multi-token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx new file mode 100644 index 00000000..d67a74e2 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -0,0 +1,210 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "Burn ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens within a Compose diamond. + + + +- Supports burning of ERC721 tokens by ID. +- Emits standard `Transfer` events with `to` address set to the zero address upon burning. +- Integrates seamlessly with the Compose diamond storage pattern. + + +## Overview + +The ERC721BurnFacet provides the functionality to destroy ERC721 tokens directly within a Compose diamond. It integrates with the diamond's storage pattern to manage token states and emits standard ERC721 events upon successful burning. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721BurnFacet} from "../facets/ERC721BurnFacet.sol"; +import {IDiamondProxy} from "@compose-protocol/diamond-proxy/contracts/IDiamondProxy.sol"; + +contract ERC721BurnConsumer { + IDiamondProxy public diamondProxy; + + constructor(address _diamondProxy) { + diamondProxy = IDiamondProxy(_diamondProxy); + } + + function burnToken(uint256 _tokenId) external { + bytes4 selector = IERC721BurnFacet.burn.selector; + // Note: The \`burn\` function requires approval or ownership. + // This example assumes the caller has the necessary permissions. + (bool success, ) = address(diamondProxy).call(abi.encodeWithSelector(selector, _tokenId)); + require(success, "ERC721BurnFacet: burn failed"); + } +}`} + + +## Best Practices + + +- Ensure the caller has ownership of the token or is approved to burn it before invoking the `burn` function via the diamond proxy. +- Properly initialize the diamond with this facet to enable token burning capabilities. + + +## Security Considerations + + +The `burn` function requires the caller to have the necessary permissions (token ownership or operator approval). Insufficient approval will lead to a revert with `ERC721InsufficientApproval`. Access control is managed by the underlying ERC721 implementation within the diamond. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx new file mode 100644 index 00000000..4d3f8eab --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -0,0 +1,662 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "Manages ERC721 token standard operations within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC721 token standard operations within a diamond. + + + +- Implements ERC721 standard for NFTs. +- Supports token transfers, approvals, and ownership queries. +- Provides access to token metadata via `tokenURI`. +- Includes internal transfer logic for composability. + + +## Overview + +The ERC721Facet provides the core functionality for managing non-fungible tokens (NFTs) on a Compose diamond. It implements the ERC721 standard, enabling token ownership tracking, transfers, approvals, and metadata retrieval, making the diamond capable of acting as an NFT collection contract. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer a token, checking for ownership and approval. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose/diamond/facets/ERC721/IERC721Facet.sol"; +import {IDiamondRegistry} from "@compose/diamond/IDiamondRegistry.sol"; + +contract MyERC721Diamond is IDiamondRegistry { + address constant ERC721_FACET_ADDRESS = address(0xYourERC721FacetAddress); + + function name() external view returns (string memory) { + return IERC721Facet(ERC721_FACET_ADDRESS).name(); + } + + function symbol() external view returns (string memory) { + return IERC721Facet(ERC721_FACET_ADDRESS).symbol(); + } + + function ownerOf(uint256 tokenId) external view returns (address) { + return IERC721Facet(ERC721_FACET_ADDRESS).ownerOf(tokenId); + } + + function transferFrom(address from, address to, uint256 tokenId) external { + IERC721Facet(ERC721_FACET_ADDRESS).transferFrom(from, to, tokenId); + } +}`} + + +## Best Practices + + +- Deploy the ERC721Facet and register its selectors with the diamond proxy. +- Ensure the diamond has adequate access control for functions like `approve` and `transferFrom` if necessary. +- Handle token URIs carefully to ensure metadata consistency and immutability where intended. + + +## Security Considerations + + +The `internalTransferFrom` function includes critical checks for ownership and approvals. Ensure that access to functions like `approve` and `setApprovalForAll` is appropriately managed to prevent unauthorized token control. The `safeTransferFrom` functions include checks for receiver contract compatibility, mitigating reentrancy risks from malicious receiver contracts. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx new file mode 100644 index 00000000..0f303f35 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -0,0 +1,358 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "Manage ERC-721 tokens within a diamond proxy." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-721 tokens within a diamond proxy. + + + +- Permissionless minting and burning of ERC-721 tokens. +- Handles token ownership and approval logic internally. +- Utilizes diamond storage to maintain a consistent token state across facets. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod provides core logic for managing ERC-721 compliant tokens. It abstracts away the complexities of token state management, allowing facets to implement ERC-721 functionality by interacting with shared diamond storage. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256 balance) balanceOf; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod } from "@compose/core/src/modules/ERC721Mod.sol"; + +contract MyERC721Facet { + IERC721Mod internal erc721Mod; + + function initialize(address _erc721ModAddress) external { + erc721Mod = IERC721Mod(_erc721ModAddress); + } + + function mintToken(address _to, uint256 _tokenId) external { + erc721Mod.mint(_to, _tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + erc721Mod.transferFrom(_from, _to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + erc721Mod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721Mod` is initialized correctly via its facet's `initialize` function before calling any of its methods. +- Handle potential `ERC721` custom errors to provide informative feedback to users during token operations. +- Be mindful of gas costs associated with token transfers and burns, especially in loops. + + +## Integration Notes + + +ERC721Mod interacts with diamond storage via a predefined storage slot. Facets using this module should ensure they do not conflict with this slot. The `getStorage` function can be used to inspect the raw ERC-721 storage, though direct manipulation is discouraged in favor of module functions. The module maintains invariants related to token existence, ownership, and approvals. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json new file mode 100644 index 00000000..219beb4e --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC721/ERC721/index" + } +} diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx new file mode 100644 index 00000000..db4f299f --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-721" +description: "ERC-721 non-fungible token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-721 non-fungible token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..0622e03a --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,217 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "Burn ERC721 tokens and manage enumeration state" +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens and manage enumeration state + + + +- Enables the burning of ERC721 tokens. +- Automatically updates internal enumeration tracking upon token burn. +- Emits a `Transfer` event for burned tokens, signifying their removal from circulation. + + +## Overview + +This facet provides functionality to burn ERC721 tokens. It integrates with the enumeration tracking mechanisms to ensure that burned tokens are correctly removed from the tracked list, maintaining the integrity of the token supply. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurn } from "@compose-protocol/core/contracts/facets/ERC721/IERC721EnumerableBurn.sol"; + +contract ExampleConsumer { + IERC721EnumerableBurn private constant ERC721_ENUMERABLE_BURN_FACET = IERC721EnumerableBurn(0x...); // Diamond proxy address + + function burnToken(uint256 tokenId) external { + // Assume appropriate approvals and ownership checks are handled by access control or other facets + ERC721_ENUMERABLE_BURN_FACET.burn(tokenId); + } +}`} + + +## Best Practices + + +- Ensure proper access control is implemented before calling the `burn` function to prevent unauthorized token destruction. +- Integrate with other ERC721 facets (like `ERC721Receiver`) to handle post-burn logic if necessary. + + +## Security Considerations + + +The `burn` function requires careful access control to prevent unauthorized burning of tokens. Ensure that only the token owner or an authorized operator can call this function. The `ERC721NonexistentToken` error is raised if the token ID does not exist, and `ERC721InsufficientApproval` is raised if the caller does not have sufficient approval to burn the token. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..df881122 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -0,0 +1,737 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "Enumerable ERC-721 functionality for token management." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enumerable ERC-721 functionality for token management. + + + +- Tracks total token supply and individual owner balances. +- Allows retrieval of token IDs by owner and index, enabling enumeration. +- Supports standard ERC-721 metadata and approval mechanisms. + + +## Overview + +This facet provides standard ERC-721 enumerable features, allowing for the tracking of total supply, token ownership counts, and specific token IDs by owner and index. It integrates seamlessly into a Compose diamond, extending its capabilities with a comprehensive NFT implementation. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage struct used by this facet. + + +{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### internalTransferFrom + +Internal function to transfer ownership of a token ID. + + +{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; + +contract ERC721EnumerableConsumer { + IERC721EnumerableFacet public immutable erc721Facet; + + constructor(address _diamondAddress) { + erc721Facet = IERC721EnumerableFacet(_diamondAddress); + } + + function getTotalSupply() external view returns (uint256) { + return erc721Facet.totalSupply(); + } + + function getOwnerBalance(address _owner) external view returns (uint256) { + return erc721Facet.balanceOf(_owner); + } + + function getTokenByIndex(uint256 _index) external view returns (uint256) { + // Assuming a valid owner address is known or derived + // For demonstration, let's assume a known owner + address owner = address(0x123); // Replace with actual owner derivation + return erc721Facet.tokenOfOwnerByIndex(owner, _index); + } +}`} + + +## Best Practices + + +- Ensure proper initialization of the ERC721 storage within the diamond's deployment process. +- Utilize the `internalTransferFrom` function internally for state transitions to maintain consistency. +- Grant `approve` and `setApprovalForAll` permissions judiciously to manage token access. + + +## Security Considerations + + +Access control for `approve` and `setApprovalForAll` is crucial to prevent unauthorized token management. Ensure that token transfers (`transferFrom`, `safeTransferFrom`) are routed through the diamond proxy to maintain state integrity and enforce access controls. Be mindful of potential reentrancy if external contracts interact directly with token URIs or metadata. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..6ab9c144 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -0,0 +1,342 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "Manages enumerable ERC-721 token state within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages enumerable ERC-721 token state within a diamond. + + + +- Manages token IDs for enumeration (e.g., `tokenByIndex`). +- Handles state updates for `mint`, `burn`, and `transferFrom` operations. +- Provides access to the internal ERC-721 enumerable storage struct. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721EnumerableMod provides the core logic for tracking and managing ERC-721 token ownership and enumeration. It ensures that minted, burned, and transferred tokens are correctly reflected in the token count and ownership lists, enabling compliant ERC-721 behavior within a diamond proxy. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { +mapping(uint256 tokenId => address owner) ownerOf; +mapping(address owner => uint256[] ownerTokens) ownerTokens; +mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; +uint256[] allTokens; +mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; +mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; +mapping(uint256 tokenId => address approved) approved; +string name; +string symbol; +string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "@compose/modules/ERC721EnumerableMod.sol"; +import {ERC721Storage} from "@compose/storage/ERC721Storage.sol"; + +contract MyERC721Facet { + IERC721EnumerableMod public immutable erc721Mod; + + constructor(address _diamondProxy) { + erc721Mod = IERC721EnumerableMod(_diamondProxy); + } + + function mintToken(address _to, uint256 _tokenId) external { + erc721Mod.mint(_to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + erc721Mod.burn(_tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + erc721Mod.transferFrom(_from, _to, _tokenId); + } +}`} + + +## Best Practices + + +- Ensure correct ownership and approval checks are performed before calling `transferFrom` or `burn`. +- Handle potential reverts from `mint`, `burn`, and `transferFrom` appropriately in facet logic. +- Understand that `ERC721EnumerableMod` modifies shared ERC-721 storage; coordinate with other ERC-721 facets. + + +## Integration Notes + + +This module interacts with the `ERC721Storage` struct, specifically managing arrays for token enumeration. Facets using this module should be aware that operations like minting and burning modify shared state visible to all ERC-721 facets. The module relies on the standard ERC-721 storage layout and slot definitions. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json new file mode 100644 index 00000000..fdc633f9 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721 Enumerable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC721/ERC721Enumerable/index" + } +} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx new file mode 100644 index 00000000..8ae400ee --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-721 Enumerable" +description: "ERC-721 Enumerable extension for ERC-721 tokens." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-721 Enumerable extension for ERC-721 tokens. + + +_No items in this category yet._ diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json new file mode 100644 index 00000000..8ee4f288 --- /dev/null +++ b/website/docs/library/token/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC721/index" + } +} diff --git a/website/docs/library/token/ERC721/index.mdx b/website/docs/library/token/ERC721/index.mdx new file mode 100644 index 00000000..db4f299f --- /dev/null +++ b/website/docs/library/token/ERC721/index.mdx @@ -0,0 +1,14 @@ +--- +title: "ERC-721" +description: "ERC-721 non-fungible token implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-721 non-fungible token implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx new file mode 100644 index 00000000..ecc7520d --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -0,0 +1,191 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "Manages token royalties according to ERC-2981." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages token royalties according to ERC-2981. + + + +- Implements ERC-2981 `royaltyInfo` function. +- Supports token-specific and default royalty configurations. +- Calculates royalty amounts based on sale price using basis points. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, enabling royalty payments for NFTs sold on Compose diamonds. It provides functions to retrieve royalty information for specific tokens or a default royalty configuration, ensuring creators are compensated on secondary market sales. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyFacet} from "@compose-protocol/diamond/contracts/facets/Royalty/IRoyaltyFacet.sol"; + +contract RoyaltyConsumer { + address immutable DIAMOND_ADDRESS; + bytes4 private constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function getRoyaltyDetails(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 royaltyAmount) { + bytes memory data = abi.encodeWithSelector(ROYALTY_INFO_SELECTOR, tokenId, salePrice); + (bool success, bytes memory returnData) = DIAMOND_ADDRESS.staticcall(data); + require(success, "Royalty query failed"); + (receiver, royaltyAmount) = abi.decode(returnData, (address, uint256)); + return (receiver, royaltyAmount); + } +}`} + + +## Best Practices + + +- Integrate the RoyaltyFacet into your diamond to enable ERC-2981 compliant royalty distributions. +- Configure token-specific royalties or a default royalty rate during diamond initialization. + + +## Security Considerations + + +The `royaltyInfo` function is read-only and uses `staticcall`, mitigating reentrancy risks. Ensure the royalty basis points and receiver addresses are correctly configured during deployment and upgrades to prevent unintended royalty distributions. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx new file mode 100644 index 00000000..d2af8687 --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -0,0 +1,354 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "Manages ERC-2981 royalties with token-specific and default settings." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-2981 royalties with token-specific and default settings. + + + +- Supports both default and token-specific royalty configurations. +- Implements ERC-2981 `royaltyInfo` function logic, including fallback to default settings. +- Provides functions to set, delete, and reset royalty information. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides a robust implementation for ERC-2981 royalty standards, enabling both default royalty settings and token-specific overrides. It ensures that royalty information is always retrievable, either from token-specific configurations or a fallback default, enhancing composability for NFT marketplaces and secondary sales. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { +address receiver; +uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { +RoyaltyInfo defaultRoyaltyInfo; +mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "./interfaces/IRoyaltyMod.sol"; + +contract RoyaltyConsumerFacet { + address immutable DIAMOND_ADDRESS; + IRoyaltyMod immutable royaltyMod; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + royaltyMod = IRoyaltyMod(diamondAddress); + } + + /** + * @notice Sets a royalty for a specific token. + * @param _tokenId The ID of the token. + * @param _receiver The address to receive royalties. + * @param _feeBasisPoints The royalty fee in basis points. + */ + function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { + royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + } + + /** + * @notice Gets royalty information for a token. + * @param _tokenId The ID of the token. + * @param _salePrice The sale price of the token. + * @return receiver The address receiving royalties. + * @return feeAmount The amount of royalties to be paid. + */ + function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 feeAmount) { + return royaltyMod.royaltyInfo(_tokenId, _salePrice); + } +}`} + + +## Best Practices + + +- Ensure receiver addresses are validated before setting royalties to prevent unintended distributions. +- Utilize `deleteDefaultRoyalty` and `resetTokenRoyalty` judiciously to manage royalty configurations effectively. +- Be aware that royalty information is accessed via the diamond proxy, ensuring consistent behavior across facet upgrades. + + +## Integration Notes + + +The RoyaltyMod stores its state in a dedicated slot within the diamond's storage. The `getStorage` function provides direct access to this storage struct. Facets can interact with royalty logic by calling the external functions exposed by this module through the diamond proxy. Changes to default or token-specific royalties are immediately reflected in subsequent calls to `royaltyInfo`. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json new file mode 100644 index 00000000..cb6b460f --- /dev/null +++ b/website/docs/library/token/Royalty/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Royalty", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/Royalty/index" + } +} diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx new file mode 100644 index 00000000..8ae51d9d --- /dev/null +++ b/website/docs/library/token/Royalty/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Royalty" +description: "ERC-2981 royalty standard implementations." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-2981 royalty standard implementations. + + +_No items in this category yet._ diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json new file mode 100644 index 00000000..3f26c2ce --- /dev/null +++ b/website/docs/library/token/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Token Standards", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/index" + } +} diff --git a/website/docs/library/token/index.mdx b/website/docs/library/token/index.mdx new file mode 100644 index 00000000..50f27698 --- /dev/null +++ b/website/docs/library/token/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Token Standards" +description: "Token standard implementations for Compose diamonds." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Token standard implementations for Compose diamonds. + + +_No items in this category yet._ diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx new file mode 100644 index 00000000..047c1f79 --- /dev/null +++ b/website/docs/library/utils/NonReentrancyMod.mdx @@ -0,0 +1,139 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "Prevent reentrant calls within diamond functions." +gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Prevent reentrant calls within diamond functions. + + + +- Provides `enter()` and `exit()` functions to manage reentrancy state. +- Uses a simple state variable to track reentrancy status, minimizing storage overhead. +- Designed for easy integration into any facet. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides essential guards to prevent reentrant function calls, a common vulnerability in smart contracts. By integrating these checks, facets can ensure that sensitive operations are not executed multiple times before the initial execution completes, maintaining data integrity and preventing unexpected state changes within the diamond. + +--- + +## Storage + +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/contracts/src/module/lib/LibNonReentrancy.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 internal _nonReentrancyState; + + /** + * @notice Example function protected by non-reentrancy guard. + */ + function protectedAction() external { + // Lock reentrancy before executing sensitive logic. + _nonReentrancyState.enter(); + + // ... sensitive logic here ... + + // Unlock reentrancy after sensitive logic is complete. + _nonReentrancyState.exit(); + } +}`} + + +## Best Practices + + +- Always call `enter()` at the beginning of a function before any external calls or state-changing operations that could lead to reentrancy. +- Always call `exit()` at the end of a function after all sensitive operations are completed and before returning. +- Ensure the state variable used for tracking reentrancy is unique to the function or context being protected. + + +## Integration Notes + + +The `LibNonReentrancy` library operates on a `uint256` storage slot. Facets should use a dedicated `uint256` variable within their own storage to manage the non-reentrancy state for specific functions. The `enter()` function will revert if reentrancy is detected, ensuring that the function's execution is atomic and safe. The `exit()` function is crucial for resetting the state after successful execution. + + +
+ +
+ + diff --git a/website/docs/library/utils/_category_.json b/website/docs/library/utils/_category_.json new file mode 100644 index 00000000..d9c087be --- /dev/null +++ b/website/docs/library/utils/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Utilities", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/utils/index" + } +} diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx new file mode 100644 index 00000000..2ebbc3b4 --- /dev/null +++ b/website/docs/library/utils/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Utilities" +description: "Utility libraries and helpers for diamond development." +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Utility libraries and helpers for diamond development. + + +_No items in this category yet._ From 2dfa9eb69851162421010a7e56bd0fa891a9311a Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 16:41:59 -0500 Subject: [PATCH 50/68] generate index after pages --- .github/scripts/generate-docs.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index 9099a0c9..63c6890f 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -38,7 +38,7 @@ const { } = require('./generate-docs-utils/forge-doc-parser'); const { generateFacetDoc, generateModuleDoc } = require('./generate-docs-utils/templates/templates'); const { enhanceWithAI, shouldSkipEnhancement, addFallbackContent } = require('./generate-docs-utils/ai-enhancement'); -const { syncDocsStructure } = require('./generate-docs-utils/category-generator'); +const { syncDocsStructure, regenerateAllIndexFiles } = require('./generate-docs-utils/category-generator'); // ============================================================================ // Tracking @@ -440,7 +440,18 @@ async function main() { console.log(''); } - // Step 4: Print summary + // Step 4: Regenerate all index pages now that docs are created + console.log('📄 Regenerating category index pages...'); + const indexResult = regenerateAllIndexFiles(true); + if (indexResult.regenerated.length > 0) { + console.log(` Regenerated ${indexResult.regenerated.length} index pages`); + if (indexResult.regenerated.length <= 10) { + indexResult.regenerated.forEach((c) => console.log(` ✅ ${c}`)); + } + } + console.log(''); + + // Step 5: Print summary printSummary(); writeSummaryFile(); } From fb2d50b43c135cf21f07baecee55b099ca124b94 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sun, 21 Dec 2025 21:47:30 +0000 Subject: [PATCH 51/68] docs: auto-generate docs pages from NatSpec --- .../AccessControl/AccessControlFacet.mdx | 67 +++++++------- .../access/AccessControl/AccessControlMod.mdx | 54 ++++++----- .../library/access/AccessControl/index.mdx | 17 +++- .../AccessControlPausableFacet.mdx | 60 ++----------- .../AccessControlPausableMod.mdx | 49 +++++----- .../access/AccessControlPausable/index.mdx | 17 +++- .../AccessControlTemporalFacet.mdx | 88 ++++++++++-------- .../AccessControlTemporalMod.mdx | 50 +++++------ .../access/AccessControlTemporal/index.mdx | 17 +++- .../docs/library/access/Owner/OwnerFacet.mdx | 59 ++---------- .../docs/library/access/Owner/OwnerMod.mdx | 56 ++++++------ website/docs/library/access/Owner/index.mdx | 17 +++- .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 47 +++++----- .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 60 +++++++------ .../library/access/OwnerTwoSteps/index.mdx | 17 +++- website/docs/library/access/index.mdx | 38 +++++++- .../docs/library/diamond/DiamondCutFacet.mdx | 62 ++++++------- .../docs/library/diamond/DiamondCutMod.mdx | 89 +++++++------------ .../library/diamond/DiamondLoupeFacet.mdx | 36 ++++---- website/docs/library/diamond/DiamondMod.mdx | 50 +++++------ .../diamond/example/ExampleDiamond.mdx | 69 +++++++------- .../docs/library/diamond/example/index.mdx | 10 ++- website/docs/library/diamond/index.mdx | 38 +++++++- website/docs/library/index.mdx | 38 +++++++- .../interfaceDetection/ERC165/ERC165Mod.mdx | 53 ++++++----- .../interfaceDetection/ERC165/index.mdx | 10 ++- .../docs/library/interfaceDetection/index.mdx | 10 ++- .../library/token/ERC1155/ERC1155Facet.mdx | 65 +++++++------- .../docs/library/token/ERC1155/ERC1155Mod.mdx | 46 +++++----- website/docs/library/token/ERC1155/index.mdx | 17 +++- .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 40 ++++----- .../library/token/ERC20/ERC20/ERC20Facet.mdx | 64 +++++++------ .../library/token/ERC20/ERC20/ERC20Mod.mdx | 53 +++++------ .../docs/library/token/ERC20/ERC20/index.mdx | 24 ++++- .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 84 ++++++++--------- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 61 +++++-------- .../token/ERC20/ERC20Bridgeable/index.mdx | 17 +++- .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 63 +++++-------- .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 63 ++++++++----- .../library/token/ERC20/ERC20Permit/index.mdx | 17 +++- website/docs/library/token/ERC20/index.mdx | 24 ++++- .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 52 +++++------ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 45 +++++----- .../library/token/ERC6909/ERC6909/index.mdx | 17 +++- website/docs/library/token/ERC6909/index.mdx | 10 ++- .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 44 ++++----- .../token/ERC721/ERC721/ERC721Facet.mdx | 55 ++++++------ .../library/token/ERC721/ERC721/ERC721Mod.mdx | 38 ++++---- .../library/token/ERC721/ERC721/index.mdx | 24 ++++- .../ERC721EnumerableBurnFacet.mdx | 40 +++++---- .../ERC721EnumerableFacet.mdx | 46 +++++----- .../ERC721Enumerable/ERC721EnumerableMod.mdx | 44 ++++----- .../token/ERC721/ERC721Enumerable/index.mdx | 24 ++++- website/docs/library/token/ERC721/index.mdx | 17 +++- .../library/token/Royalty/RoyaltyFacet.mdx | 55 +++++++----- .../docs/library/token/Royalty/RoyaltyMod.mdx | 56 +++++------- website/docs/library/token/Royalty/index.mdx | 17 +++- website/docs/library/token/index.mdx | 38 +++++++- .../docs/library/utils/NonReentrancyMod.mdx | 45 +++++----- website/docs/library/utils/index.mdx | 10 ++- 60 files changed, 1423 insertions(+), 1070 deletions(-) diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx index 232d9d68..d8c281fe 100644 --- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlFacet" -description: "Manage roles and permissions within the diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControl/AccessControlFacet.sol" +description: "Manages roles and permissions within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControl/AccessControlFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage roles and permissions within the diamond. +Manages roles and permissions within a diamond. -- Role-based access control system. -- Support for granting, revoking, and renouncing roles. -- Batch operations for efficient role management. -- Role admin hierarchy for decentralized control. +- Role-based access control (RBAC) system. +- Granular permission management through roles and accounts. +- Support for granting, revoking, and checking roles. +- Admin role hierarchy for role management. ## Overview -The AccessControlFacet provides a robust, role-based access control mechanism for Compose diamonds. It allows for the definition of roles, assignment of roles to accounts, and enforcement of role-based permissions on function calls. This facet is crucial for securing sensitive operations and managing administrative privileges within the diamond. +The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling developers to define roles, assign them to accounts, and enforce access restrictions on functions. This facet is crucial for orchestrating secure interactions within the diamond. --- @@ -500,48 +500,55 @@ error AccessControlUnauthorizedSender(address _sender, address _account); {`pragma solidity ^0.8.30; -import {DiamondLoupeFacet} from "@compose-diamond/diamond-loupe/contracts/DiamondLoupeFacet.sol"; -import {AccessControlFacet} from "@compose-diamond/access-control/contracts/AccessControlFacet.sol"; +import {DiamondLoupeFacet} from "@compose-protocol/diamond/facets/DiamondLoupe/DiamondLoupeFacet.sol"; +import {AccessControlFacet} from "@compose-protocol/diamond/facets/AccessControl/AccessControlFacet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond/contracts/interfaces/IDiamondCut.sol"; -contract MyDiamond is DiamondLoupeFacet { - // Facet definition can be found in the Compose Diamond template - // ... +contract DeployAccessControl { + // Assume diamondAddress is the address of your deployed diamond proxy + address diamondAddress; - function setAdminRole(address _diamondAdmin, bytes4 _functionSelector, bytes32 _adminRole) external { - AccessControlFacet ac = AccessControlFacet(address(this)); - // Ensure caller is the diamond admin or has the role to change role admins - ac.setRoleAdmin(_adminRole); // This is an example, actual implementation may differ based on role admin logic + function deploy() external { + // ... deployment logic ... + + // Example: Granting ROLE_ADMIN to the deployer + AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress); + bytes32 adminRole = accessControlFacet.getRoleAdmin(keccak256("ROLE_USER")); + accessControlFacet.grantRole(adminRole, msg.sender); + + // Example: Revoking ROLE_USER from a specific account + address userToRevoke = address(0x123); + accessControlFacet.revokeRole(keccak256("ROLE_USER"), userToRevoke); } - function grantAccess(address _account, bytes32 _role) external { - AccessControlFacet ac = AccessControlFacet(address(this)); - ac.grantRole(_role, _account); + function checkPermission(address _account, bytes32 _role) public view returns (bool) { + AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress); + return accessControlFacet.hasRole(_role, _account); } - function checkPermission(address _account, bytes32 _role) external view returns (bool) { - AccessControlFacet ac = AccessControlFacet(address(this)); - return ac.hasRole(_role, _account); + function enforcePermission(address _account, bytes32 _role) public view { + AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress); + accessControlFacet.requireRole(_role, _account); } -} -`} +}`} ## Best Practices -- Define roles clearly and manage their admin roles strategically. The `DEFAULT_ADMIN_ROLE` typically controls the management of other roles. -- Utilize `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple accounts for a single role. -- Implement role checks directly within functions or use internal helper functions to enforce access control consistently. +- Define roles as `bytes32` constants for clarity and consistency. +- Use batch functions (`grantRoleBatch`, `revokeRoleBatch`) for efficiency when managing multiple accounts for the same role. +- Carefully manage the `ROLE_ADMIN` role, as it controls the ability to modify role assignments. ## Security Considerations -Ensure that only authorized accounts can grant or revoke roles, especially for administrative roles. The `AccessControlUnauthorizedAccount` error indicates a caller attempting an action without the necessary role. `AccessControlUnauthorizedSender` is used when an account tries to renounce a role it does not possess. Be mindful of potential reentrancy if facet functions are called within external contracts without proper checks. +Ensure that the caller has the necessary permissions to grant or revoke roles. Unauthorized calls to `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch` will revert. The `renounceRole` function can only be called by the account requesting to renounce the role.
- + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx index 833abee0..8bc65da2 100644 --- a/website/docs/library/access/AccessControl/AccessControlMod.mdx +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "AccessControlMod" description: "Manage roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControl/AccessControlMod.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControl/AccessControlMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,9 +25,9 @@ Manage roles and permissions within a diamond. -- Role-based access control for diamond functions. -- Granting, revoking, and checking of roles for accounts. -- Ability to set and manage administrative roles for other roles. +- Role-based access control for granular permission management. +- Functions for granting, revoking, and checking role assignments. +- Ability to define and manage administrative roles for other roles. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The AccessControl module provides a robust mechanism for managing role-based access control within Compose diamonds. It enables granular permission management by allowing roles to be granted to accounts and checked against specific functions. This module is crucial for securing diamond functionality and ensuring that only authorized accounts can perform sensitive operations. +The AccessControl module provides a robust framework for managing role-based access control within Compose diamonds. It enables granular permission management, ensuring that only authorized accounts can execute specific functions. This module is crucial for securing diamond functionality and maintaining a clear separation of administrative privileges. --- @@ -401,22 +401,34 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControl} from "@compose-protocol/diamond-contracts/contracts/modules/access-control/IAccessControl.sol"; +import {IAccessControlMod} from "@compose/diamond/modules/access-control/IAccessControlMod.sol"; contract MyFacet { - IAccessControl public accessControl; - - // Assume accessControl is initialized to the diamond's AccessControl facet address - - function performAdminTask() external { - // Check if the caller has the ADMIN_ROLE before proceeding - accessControl.requireRole(accessControl.getStorage().ADMIN_ROLE, msg.sender); + IAccessControlMod internal accessControl; + + // Assuming accessControl is initialized externally and its storage slot is known + constructor(address _diamondProxy, bytes32 _accessControlStorageSlot) { + accessControl = IAccessControlMod(_diamondProxy); + // To interact with the AccessControl module, you would typically call its functions directly + // via the diamond proxy, which routes calls to the correct facet. The storage slot is + // generally not directly used by facets but is relevant for deployment and upgrades. + } - // ... perform admin task ... + /** + * @notice Grants the DEFAULT_ADMIN_ROLE to the caller. + */ + function initialize() external { + bytes32 adminRole = accessControl.getStorage().DEFAULT_ADMIN_ROLE; + accessControl.grantRole(adminRole, msg.sender); } - function grantNewRole(address _account, bytes32 _role) external { - accessControl.grantRole(_role, _account); + /** + * @notice Requires the caller to have the MINTER_ROLE. + */ + function mintToken(address _to, uint256 _amount) external { + bytes32 minterRole = accessControl.getStorage().MINTER_ROLE; // Assuming MINTER_ROLE is defined + accessControl.requireRole(minterRole, msg.sender); + // ... minting logic ... } }`} @@ -424,19 +436,19 @@ contract MyFacet { ## Best Practices -- Use `requireRole` with custom errors for explicit access control checks within facets. -- Store role identifiers (`bytes32`) as constants for maintainability and to prevent typos. -- Ensure the `ADMIN_ROLE` is appropriately protected and managed during diamond upgrades. +- Use `requireRole` to enforce access control checks at the beginning of sensitive functions. +- Carefully manage role assignments and revocations, especially for administrative roles. +- Understand that role changes are state-changing operations and should be audited. ## Integration Notes -The AccessControl module utilizes a dedicated storage slot within the diamond. Facets interact with this module by calling its external functions, such as `grantRole`, `revokeRole`, `hasRole`, and `requireRole`. The `getStorage` function provides direct access to the module's internal storage structure, allowing for programmatic inspection of role assignments and configurations. Changes to roles made through the AccessControl facet are immediately reflected for all other facets interacting with the diamond. +The AccessControl module manages its state within a dedicated storage slot. Facets interact with the module's functionality by calling its functions through the diamond proxy. The `getStorage()` function provides read access to the module's internal storage structure. Any changes to roles made via `grantRole`, `revokeRole`, or `setRoleAdmin` are persistent and visible to all facets interacting with the diamond.
- + diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx index 73addea3..34964496 100644 --- a/website/docs/library/access/AccessControl/index.mdx +++ b/website/docs/library/access/AccessControl/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; Role-based access control (RBAC) pattern. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx index 579b3029..f4872463 100644 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlPausableFacet" -description: "Manages role-based access control and pausing functionality for diamond modules." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +description: "Access Control Pausable facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlPausable/AccessControlPausableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages role-based access control and pausing functionality for diamond modules. +Access Control Pausable facet for Compose diamonds -- Role-specific pausing and unpausing of permissions. -- Granular control over module availability based on role status. -- Reverts with specific errors for unauthorized access or paused roles. +- Self-contained facet with no imports or inheritance +- Only `external` and `internal` function visibility +- Follows Compose readability-first conventions +- Ready for diamond integration ## Overview -The AccessControlPausableFacet extends Compose's access control by introducing role-specific pausing. This allows granular control over module functionality, enabling temporary deactivation of roles without affecting the entire diamond. It provides essential functions for checking pause status, pausing, and unpausing roles, ensuring robust operational control. +Access Control Pausable facet for Compose diamonds --- @@ -321,51 +322,8 @@ error AccessControlRolePaused(bytes32 _role); -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {AccessControlPausableFacet} from "@compose/contracts/facets/AccessControl/AccessControlPausableFacet.sol"; -import {IDiamondCut} from "@compose/contracts/diamond/IDiamondCut.sol"; - -contract Deployer { - function deployAccessControlPausable() external { - // Assume diamondCut is an instance of IDiamondCut - IDiamondCut diamondCut; - - // Add the AccessControlPausableFacet - diamondCut.execute(IDiamondCut.DiamondCutArgs( - IDiamondCut.FacetCut[]( - IDiamondCut.FacetCut(address(new AccessControlPausableFacet()), IDiamondCut.FacetCutAction.ADD, new bytes[](0)) - ), - address(0), // No delete action - new bytes[](0) // No init data - )); - - // Example of pausing a role (assuming 'CALLER' is a defined role) - // address accessControlPausableFacetAddress = diamondCut.getFacetAddress(AccessControlPausableFacet.FUNC_PAUSEROLE); - // AccessControlPausableFacet(accessControlPausableFacetAddress).pauseRole("CALLER"); - } -}`} - - -## Best Practices - - -- Integrate this facet to enforce role-based access control with the ability to temporarily disable specific roles. -- Use `requireRoleNotPaused` within other facets to ensure operations are only permitted when their associated roles are active. -- Ensure the diamond's admin or the designated role admin is correctly configured to manage pausing and unpausing operations. - - -## Security Considerations - - -Access to `pauseRole` and `unpauseRole` is restricted to the admin of the respective role, preventing unauthorized pausing. The `requireRoleNotPaused` function ensures that calls to protected functions fail if the caller's role is paused, mitigating reentrancy risks related to paused states. Input validation on role names is crucial. - -
- + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx index 1b876edb..0ded9969 100644 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlPausableMod" -description: "Manage role-based access control with pausing capabilities." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlPausable/AccessControlPausableMod.sol" +description: "Manage role pausing and unpausing for access control." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlPausable/AccessControlPausableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage role-based access control with pausing capabilities. +Manage role pausing and unpausing for access control. -- Role-specific pausing: allows granular control over operational suspension. -- Composable with existing access control mechanisms. -- Reverts with specific errors (`AccessControlRolePaused`, `AccessControlUnauthorizedAccount`) for clear error handling. +- Allows granular pausing and unpausing of individual roles. +- Provides a `requireRoleNotPaused` check that reverts if the role is paused or the caller lacks the role. +- Integrates with existing access control mechanisms. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module extends diamond access control by introducing role-specific pausing. It allows administrators to temporarily halt operations for specific roles, ensuring critical functions can be suspended during emergencies or maintenance without affecting the entire diamond. This composition pattern enhances security and operational flexibility. +This module provides functionality to pause and unpause specific roles within an access control system. By pausing a role, all operations requiring that role are temporarily blocked, enhancing safety during upgrades or critical operations. It ensures that sensitive actions are only permitted when the relevant role is active. --- @@ -330,25 +330,24 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlPausableMod} from "@compose/modules/access-control-pausable/IAccessControlPausableMod.sol"; +import {IAccessControlPausableMod} from "@compose/modules/access-control/pausable/IAccessControlPausableMod.sol"; contract MyFacet { - IAccessControlPausableMod public constant ACCESS_CONTROL_PAUSABLE_MOD = IAccessControlPausableMod(0xaddress); // Replace with actual module address + IAccessControlPausableMod accessControlPausableMod; - bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + constructor(address _accessControlPausableMod) { + accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); + } - function pauseMyFeature() external { - // Ensure the caller has the PAUSER_ROLE and the role is not paused - ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(msg.sender, PAUSER_ROLE); - // If the role is not paused, proceed with pausing the feature - ACCESS_CONTROL_PAUSABLE_MOD.pauseRole(PAUSER_ROLE); + function grantRoleAndPause(bytes32 role, address account) external { + // Assume role granting logic exists elsewhere or is handled by another facet + accessControlPausableMod.pauseRole(role); } - function performCriticalOperation() external { - // Ensure the caller has the OPERATOR_ROLE and the role is not paused - ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(msg.sender, OPERATOR_ROLE); - // Perform the critical operation + function unpauseRoleAndPerformAction(bytes32 role) external { + accessControlPausableMod.unpauseRole(role); + // Perform action only allowed when role is not paused + accessControlPausableMod.requireRoleNotPaused(role, msg.sender); } }`} @@ -356,19 +355,19 @@ contract MyFacet { ## Best Practices -- Use `requireRoleNotPaused` to enforce role presence and ensure the role is not paused before executing sensitive operations. -- Grant the `PAUSER_ROLE` only to trusted administrative accounts. -- Monitor `RolePaused` and `RoleUnpaused` events to track state changes. +- Use `requireRoleNotPaused` to enforce that a role must be active before executing sensitive operations. +- Grant roles before pausing them to ensure continuity of access control management. +- Always unpause roles when operations are intended to resume. ## Integration Notes -This module relies on the underlying Access Control module's storage for role management. The `AccessControlPausableMod` adds its own state to manage paused roles. Facets interacting with this module should be aware that calls to `requireRoleNotPaused` will check both role membership and the paused status of that role. The module's functions are designed to be called directly by facets using the module's interface. +The `AccessControlPausableMod` module manages its state in separate storage slots, distinct from the core Access Control storage. Facets can access this state via the provided getter functions (`getAccessControlStorage`, `getStorage`). The `requireRoleNotPaused` function acts as a critical guard, ensuring that operations tied to a specific role are only executable when that role is not in a paused state. This module does not alter the fundamental role granting or revoking mechanisms of the underlying Access Control facet.
- + diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx index 81a1f58c..a1c933e1 100644 --- a/website/docs/library/access/AccessControlPausable/index.mdx +++ b/website/docs/library/access/AccessControlPausable/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; RBAC with pause functionality. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx index 6afeadb2..4e6427cf 100644 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +description: "Manages time-bound role assignments and their validation." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages time-bound role assignments within a diamond. +Manages time-bound role assignments and their validation. -- Time-bound role assignments: Roles can be granted with a specific expiration timestamp. -- Admin-controlled temporal roles: Only the designated role admin can grant or revoke temporal roles. -- Expiry checks: Functions are provided to query if a role has expired or to enforce non-expired role requirements. +- Grants roles with specific expiration timestamps. +- Provides functions to check if a role has expired or is currently valid. +- Restricts role granting and revocation to the role's admin. ## Overview -The AccessControlTemporalFacet extends Compose's access control capabilities by introducing time-limited role assignments. It allows administrators to grant roles that automatically expire after a specified timestamp, enhancing dynamic permission management. This facet provides functions to grant, revoke, and check the validity of these temporal roles. +This facet extends the standard Access Control pattern by introducing time-bound roles. It allows administrators to grant roles with specific expiry timestamps and provides mechanisms to check role validity, ensuring that access is automatically revoked when a role expires. This is crucial for managing temporary permissions within a Compose diamond. --- @@ -403,34 +403,46 @@ error AccessControlRoleExpired(bytes32 _role, address _account); {`pragma solidity ^0.8.30; -import {DiamondInit} from "@compose/diamond-init/DiamondInit.sol"; -import {DiamondCutFacet} from "@compose/diamond-cut/DiamondCutFacet.sol"; -import {AccessControlFacet} from "@compose/access-control/AccessControlFacet.sol"; -import {AccessControlTemporalFacet} from "@compose/access-control/AccessControlTemporalFacet.sol"; - -contract DeployDiamond { - function deploy() external { - // ... deployment logic ... - - // Example: Granting a role with expiry - address diamondProxy = address(0x...); // Address of your deployed diamond - address admin = msg.sender; // Assuming msg.sender is the admin - bytes32 role = keccak256("MY_TEMPORARY_ROLE"); - uint64 expiryTimestamp = uint64(block.timestamp) + 3600; // Role expires in 1 hour - - // Call grantRoleWithExpiry via the diamond proxy - (bool success, bytes memory data) = diamondProxy.call( - abi.encodeWithSelector(AccessControlTemporalFacet.grantRoleWithExpiry.selector, - role, - address(1), // address to grant role to - expiryTimestamp - ) +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {AccessControlTemporalFacet} from "src/facets/AccessControlTemporalFacet.sol"; + +contract DeployExample { + address public diamondAddress; + + function deployDiamond() public { + // ... deployment logic for diamond proxy ... + diamondAddress = address(0x123); // Replace with actual diamond address + + // Add AccessControlTemporalFacet + bytes32[] memory selectors = new bytes32[](7); + selectors[0] = AccessControlTemporalFacet.grantRoleWithExpiry.selector; + selectors[1] = AccessControlTemporalFacet.revokeTemporalRole.selector; + selectors[2] = AccessControlTemporalFacet.getRoleExpiry.selector; + selectors[3] = AccessControlTemporalFacet.isRoleExpired.selector; + selectors[4] = AccessControlTemporalFacet.requireValidRole.selector; + selectors[5] = AccessControlTemporalFacet.getAccessControlStorage.selector; + selectors[6] = AccessControlTemporalFacet.getStorage.selector; + + IDiamondCut(diamondAddress).diamondCut( + IDiamondCut.FacetCut[]( + IDiamondCut.FacetCut({ + facetAddress: address(new AccessControlTemporalFacet()), + action: IDiamondCut.FacetCutAction.ADD, + isEdgeCase: false, + selectors: selectors + }) + ), + address(0), // init contract + '()' // init data ); - require(success, "Grant role failed"); + } + + function grantTemporaryRole(address account, bytes32 role, uint64 expiry) public { + AccessControlTemporalFacet(diamondAddress).grantRoleWithExpiry(role, account, expiry); + } - // Example: Checking role validity - bool isValid = AccessControlTemporalFacet(diamondProxy).isRoleExpired(role, address(1)); - require(!isValid, "Role is expired or not granted"); + function checkRole(address account, bytes32 role) public { + AccessControlTemporalFacet(diamondAddress).requireValidRole(role, account); } }`} @@ -438,19 +450,19 @@ contract DeployDiamond { ## Best Practices -- Only grant temporal roles via the `grantRoleWithExpiry` function to ensure proper event emission and access control checks. -- Utilize `isRoleExpired` or `requireValidRole` to enforce time-bound access control before critical operations. -- Store role expiry timestamps in a manner that is easily retrievable and auditable, leveraging the facet's storage accessors. +- Ensure the AccessControl facet is deployed and configured before adding this temporal facet. +- Grant roles with expiry only to trusted accounts and set appropriate expiry timestamps. +- Utilize `requireValidRole` within other facets to enforce time-bound access control checks. ## Security Considerations -This facet relies on the underlying access control mechanism for initial role granting. Ensure that the `admin` of a role is appropriately secured. The `grantRoleWithExpiry` and `revokeTemporalRole` functions are protected by access control, ensuring only the role admin can perform these actions. Input validation on the `expiryTimestamp` is crucial to prevent granting roles with invalid or immediately expired timestamps. +Access control is enforced by the role's admin. Ensure the admin role itself is secured. The `grantRoleWithExpiry` function reverts if the caller is not the admin of the specified role. The `revokeTemporalRole` function also requires the caller to be the admin. `requireValidRole` correctly reverts with `AccessControlRoleExpired` if the role has passed its expiry timestamp.
- + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx index d3ec6f6e..1d2ee032 100644 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlTemporalMod" -description: "Manages role grants with time-based expiry." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +description: "Manage time-bound role assignments in Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages role grants with time-based expiry. +Manage time-bound role assignments in Compose diamonds. -- Grants roles with a specified expiry timestamp. -- Automatically checks for role expiry when validating access via `requireValidRole`. -- Provides functions to query role expiry status and revoke temporal roles explicitly. +- Grants roles with a specific expiry timestamp. +- Automatically checks for role expiry upon validation. +- Allows explicit revocation of temporal roles. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module extends standard access control by introducing time-bound role assignments. It allows for roles to automatically expire, enhancing security and operational flexibility by ensuring temporary privileges are automatically revoked. This is crucial for managing short-term access needs within a diamond. +This module extends standard access control by introducing time-bound role assignments. It allows granting roles that automatically expire, enhancing security and operational flexibility. By integrating temporal logic, diamonds can enforce dynamic permissions, reducing the need for manual role revocation. --- @@ -433,45 +433,41 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlTemporalMod} from "@compose/modules/access-control-temporal/IAccessControlTemporalMod.sol"; -import {AccessControlFacet} from "@compose/facets/access-control/AccessControlFacet.sol"; +import {IAccessControlTemporalMod} from "@compose/diamond-protocol/src/modules/accesscontroltemporal/IAccessControlTemporalMod.sol"; -contract MyDiamond is IAccessControlTemporalMod { - // Assume Diamond ABI encoder and access control setup +contract MyDiamondFacet { + IAccessControlTemporalMod internal accessControlTemporalMod; - function grantTempAdmin(address _account, uint64 _expiry) external { - // Assuming AccessControlFacet is deployed and accessible - // and the current caller has the necessary permissions to grant roles - grantRoleWithExpiry(_account, ADMIN_ROLE, _expiry); + function grantRoleTemporarily(address _account, bytes32 _role, uint64 _expiry) external { + accessControlTemporalMod.grantRoleWithExpiry(_account, _role, _expiry); } - function enforceAdminAccess(address _account) external { - // Example of enforcing a temporal role - requireValidRole(_account, ADMIN_ROLE); - // ... proceed with admin actions ... + function checkRoleValidity(address _account, bytes32 _role) external view { + accessControlTemporalMod.requireValidRole(_account, _role); } - // Other diamond functions -} -`} + function revokeRole(address _account, bytes32 _role) external { + accessControlTemporalMod.revokeTemporalRole(_account, _role); + } +}`} ## Best Practices -- Use `requireValidRole` to enforce temporal role checks before executing sensitive operations. -- Ensure the `_expiry` timestamp provided to `grantRoleWithExpiry` is a future Unix timestamp. -- Implement logic to periodically check and revoke expired roles if automatic revocation via `requireValidRole` is not sufficient for all use cases. +- Use `requireValidRole` to enforce non-expired role checks before sensitive operations. +- Grant roles with explicit expiry timestamps to minimize stale permissions. +- Regularly audit temporal role assignments to ensure continued necessity. ## Integration Notes -This module interacts with the diamond's storage, specifically the access control and temporal access control state. Facets using this module will call its functions to manage and validate roles. The `requireValidRole` function is designed to be called within other facets to ensure that only accounts with active, non-expired roles can perform certain actions. Invariants related to role granting and revocation must be maintained across all facets that modify access control. +The AccessControlTemporalMod manages its own storage, distinct from other facets. When integrating, ensure its storage slot is correctly mapped. The `requireValidRole` function directly interacts with diamond access control logic, potentially reverting with `AccessControlUnauthorizedAccount` or `AccessControlRoleExpired`.
- + diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx index 76e7730f..1ac6b9b1 100644 --- a/website/docs/library/access/AccessControlTemporal/index.mdx +++ b/website/docs/library/access/AccessControlTemporal/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; Time-limited role-based access control. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx index a47b4435..4697bbe5 100644 --- a/website/docs/library/access/Owner/OwnerFacet.mdx +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerFacet" -description: "Manages diamond ownership and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/Owner/OwnerFacet.sol" +description: "Owner facet for Compose diamonds" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/Owner/OwnerFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages diamond ownership and transfers. +Owner facet for Compose diamonds -- Manages diamond ownership and transfers. -- Supports `transferOwnership` and `renounceOwnership`. -- Provides a view function to retrieve the current owner. +- Self-contained facet with no imports or inheritance +- Only `external` and `internal` function visibility +- Follows Compose readability-first conventions +- Ready for diamond integration ## Overview -The OwnerFacet provides essential functions for managing the ownership of a Compose diamond. It allows the current owner to transfer ownership to a new address or renounce ownership entirely, ensuring secure control over the diamond's administrative functions. +Owner facet for Compose diamonds --- @@ -162,50 +163,8 @@ error OwnerUnauthorizedAccount(); -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; - -contract OwnerUser { - IOwnerFacet ownerFacet; - - constructor(address _diamondAddress) { - ownerFacet = IOwnerFacet(_diamondAddress); - } - - function getOwner() public view returns (address) { - return ownerFacet.owner(); - } - - function transferDiamondOwnership(address _newOwner) public { - ownerFacet.transferOwnership(_newOwner); - } - - function renounceDiamondOwnership() public { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Only the current owner should call `transferOwnership` or `renounceOwnership`. -- Ensure the `_newOwner` address is valid before transferring ownership. -- Use `renounceOwnership` with caution, as it permanently relinquishes control. - - -## Security Considerations - - -Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Malicious actors cannot seize ownership without the owner's explicit action. Setting the owner to `address(0)` effectively renounces ownership, making the contract unownable and administrative functions inaccessible. - -
- + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx index e4efb3f6..93984ecb 100644 --- a/website/docs/library/access/Owner/OwnerMod.mdx +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerMod" -description: "Manages ERC-173 contract ownership." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/Owner/OwnerMod.sol" +description: "Manages contract ownership according to ERC-173." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/Owner/OwnerMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,14 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-173 contract ownership. +Manages contract ownership according to ERC-173. -- Manages ERC-173 ownership state. -- Provides `owner()` to retrieve the current owner's address. -- Includes `requireOwner()` for access control checks. -- Supports ownership transfer and renouncement via `transferOwnership()`. +- ERC-173 compliant ownership tracking. +- `owner()`: Query the current contract owner. +- `transferOwnership(_newOwner)`: Transfer ownership, including renunciation. +- `requireOwner()`: Enforce owner-only access control. @@ -37,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the core functionality for managing ERC-173 contract ownership. It defines the storage layout for the owner and offers functions to retrieve the current owner, transfer ownership, and enforce owner-only access. This is crucial for establishing administrative control within a diamond. +The OwnerMod provides essential functionality for managing contract ownership, adhering to the ERC-173 standard. It enables querying the current owner, transferring ownership, and enforcing owner-only access controls, crucial for secure contract administration within a diamond proxy. --- @@ -208,30 +208,26 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; +import {IOwnerMod} from "@compose/modules/OwnerMod.sol"; +import {DiamondStorage} from "@compose/DiamondStorage.sol"; contract MyFacet { - IOwnerMod private immutable _ownerMod; + using DiamondStorage for DiamondStorage; - constructor(address ownerModAddress) { - _ownerMod = IOwnerMod(ownerModAddress); + uint256 constant OWNER_MOD_STORAGE_SLOT = 1; // Example slot, actual value depends on deployment + + function _getOwnerMod() internal view returns (IOwnerMod) { + return IOwnerMod(address(this).staticcall(bytes4(keccak256("getStorage(uint256)")), OWNER_MOD_STORAGE_SLOT)); } - /** - * @notice Get the current owner of the contract. - */ - function getCurrentOwner() external view returns (address) { - return _ownerMod.owner(); + function checkOwnership() external view { + address currentOwner = _getOwnerMod().owner(); + // Use owner address } - /** - * @notice Transfer ownership to a new address. - * @param _newOwner The address of the new owner. - */ - function changeOwner(address _newOwner) external { - // Ensure the caller is the current owner before transferring - _ownerMod.requireOwner(); - _ownerMod.transferOwnership(_newOwner); + function transferControl(address _newOwner) external { + // Ensure caller has permission to transfer ownership (e.g., is current owner) + _getOwnerMod().transferOwnership(_newOwner); } }`} @@ -239,19 +235,19 @@ contract MyFacet { ## Best Practices -- Only the owner should call functions that modify ownership (`transferOwnership`, `setContractOwner`). Use `requireOwner()` to enforce this. -- Renounce ownership by setting the `_newOwner` to `address(0)` in `transferOwnership` if administrative control is no longer needed. -- Ensure the `OwnerMod` facet is initialized with the correct initial owner address during deployment. +- Use `requireOwner()` to restrict sensitive administrative functions to the contract owner. +- Renounce ownership by setting the new owner to `address(0)` to prevent further control. +- Ensure the OwnerMod is initialized with the correct storage slot to avoid conflicts. ## Integration Notes -The `OwnerMod` module utilizes a specific storage slot for its `OwnerModStorage` struct. Facets interacting with ownership should obtain a pointer to this storage using `getStorage()`. The `owner()` function directly reads from this storage. `transferOwnership()` and `setContractOwner()` modify this storage. Invariants related to ownership should be maintained across all facets that interact with or depend on the owner. +The OwnerMod utilizes a dedicated storage slot (defined by `STORAGE_POSITION`) to store its ownership state. Facets interact with the module via its `IOwnerMod` interface, typically by obtaining a pointer to the module's storage using `getStorage(STORAGE_POSITION)`. Changes to ownership are immediately reflected and observable by all facets.
- + diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx index 9b04676f..564c1933 100644 --- a/website/docs/library/access/Owner/index.mdx +++ b/website/docs/library/access/Owner/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; Single-owner access control pattern. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx index 034d1604..826823c8 100644 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerTwoStepsFacet" -description: "Manages ownership transfer with a two-step verification process." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +description: "Manages contract ownership with a two-step transfer process." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ownership transfer with a two-step verification process. +Manages contract ownership with a two-step transfer process. -- Two-step ownership transfer for enhanced security. -- Provides view functions for `owner()` and `pendingOwner()`. -- Supports `renounceOwnership()` to remove owner privileges. +- Two-step ownership transfer: requires initiation and acceptance. +- `renounceOwnership` allows for relinquishing ownership. +- Provides direct access to owner and pending owner addresses. ## Overview -This facet implements a secure, two-step ownership transfer mechanism for Compose diamonds. It separates the initiation and finalization of ownership changes, preventing accidental or malicious takeovers. The facet provides read access to current and pending ownership states and allows for renouncing ownership. +The OwnerTwoStepsFacet provides a secure, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are deliberate by requiring both the current owner to initiate a transfer and the new owner to accept it, preventing accidental or malicious takeovers. --- @@ -242,45 +242,52 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerTwoStepsFacet} from "@compose-protocol/diamond-contracts/facets/OwnerTwoSteps/IOwnerTwoStepsFacet.sol"; +import {IOwnerTwoStepsFacet} from "@compose/core/src/facets/ownership/IOwnerTwoStepsFacet.sol"; -contract OwnerConsumer { - IOwnerTwoStepsFacet public ownerFacet; +contract Diamond { + IOwnerTwoStepsFacet ownerFacet; - constructor(address _ownerFacetAddress) { - ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); + function setOwnerFacet(address _facetAddress) external { + ownerFacet = IOwnerTwoStepsFacet(_facetAddress); } - function initiateOwnershipTransfer(address _newOwner) public { + // Example of initiating an ownership transfer + function initiateOwnershipTransfer(address _newOwner) external { + // Assuming Diamond contract has an access control mechanism for this function ownerFacet.transferOwnership(_newOwner); } - function acceptNewOwnership() public { + // Example of accepting ownership transfer (would be called by the new owner) + function acceptOwnershipTransfer() external { ownerFacet.acceptOwnership(); } - function getCurrentOwner() public view returns (address) { + function getOwner() external view returns (address) { return ownerFacet.owner(); } + + function getPendingOwner() external view returns (address) { + return ownerFacet.pendingOwner(); + } }`} ## Best Practices -- Initialize ownership transfer using `transferOwnership(newOwner)`. -- The new owner must call `acceptOwnership()` to finalize the transfer. -- Use `renounceOwnership()` to permanently relinquish ownership. +- Initialize the facet with the current owner address during deployment. +- Ensure only the current owner can call `transferOwnership` and `renounceOwnership`. +- The pending owner must call `acceptOwnership` to finalize the transfer. ## Security Considerations -Access to `transferOwnership` is restricted to the current owner. `acceptOwnership` is restricted to the pending owner. `renounceOwnership` is restricted to the current owner. Ensure the `OwnerTwoStepsFacet` is correctly initialized with the initial owner. +Access control is critical. The `transferOwnership` function must be restricted to the current owner. The `acceptOwnership` function is intended to be called by the proposed new owner. `renounceOwnership` should also be restricted to the current owner. Incorrect access control could lead to unauthorized ownership changes. No reentrancy concerns as state changes are atomic and do not call external contracts.
- + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx index fc647417..e297565b 100644 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerTwoStepsMod" -description: "Two-step contract ownership transfer for facets." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +description: "Manages contract ownership with a two-step transfer process." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Two-step contract ownership transfer for facets. +Manages contract ownership with a two-step transfer process. -- Secure two-step ownership transfer process. -- Explicit owner acceptance mechanism. -- Capability to renounce ownership. +- Enforces a two-step ownership transfer process for enhanced security. +- Provides explicit functions to check current and pending ownership status. +- Includes a permissionless `renounceOwnership` function to permanently relinquish control. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module implements a secure, two-step ownership transfer mechanism. It prevents accidental ownership loss by requiring explicit acceptance from the new owner. This pattern is crucial for managing administrative privileges in composable diamond architectures. +This module implements a secure two-step ownership transfer mechanism. It prevents accidental ownership loss by requiring a pending owner to explicitly accept the transfer, enhancing contract safety and upgradeability. --- @@ -252,26 +252,34 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerTwoSteps} from "../interfaces/IOwnerTwoSteps.sol"; +import {IOwnerTwoSteps} from "./interfaces/IOwnerTwoSteps.sol"; -contract MyOwnerFacet { - address owner(); - function transferOwnership(address _newOwner) external { - // Call the module's transferOwnership function - IOwnerTwoSteps(msg.sender).transferOwnership(_newOwner); - } +contract MyDiamondFacet { + IOwnerTwoSteps internal ownerFacet; - function acceptOwnership() external { - // Call the module's acceptOwnership function - IOwnerTwoSteps(msg.sender).acceptOwnership(); + constructor(address _ownerFacetAddress) { + ownerFacet = IOwnerTwoSteps(_ownerFacetAddress); } - function getOwner() public view returns (address) { - return owner(); + function handleOwnershipTransfer() external { + address currentOwner = ownerFacet.owner(); + address pending = ownerFacet.pendingOwner(); + + // If there's a pending owner, the current owner can accept it. + // Otherwise, initiate a new transfer. + if (pending != address(0)) { + // Assume the current owner is calling to accept. + ownerFacet.acceptOwnership(); + } else { + // Transfer ownership to a new address. + address newOwner = address(1); // Replace with actual new owner address + ownerFacet.transferOwnership(newOwner); + } } - function renounceOwnership() external { - IOwnerTwoSteps(msg.sender).renounceOwnership(); + function protectAdminAction() external { + ownerFacet.requireOwner(); + // ... admin-specific logic ... } }`} @@ -279,19 +287,19 @@ contract MyOwnerFacet { ## Best Practices -- Use `transferOwnership` to initiate a transfer and require the new owner to call `acceptOwnership` to finalize it. -- Implement `requireOwner` checks judiciously to protect critical administrative functions. -- Be aware that `renounceOwnership` permanently removes administrative control. +- Use `transferOwnership` to initiate transfers and `acceptOwnership` to finalize them to prevent accidental ownership changes. +- Implement `requireOwner` checks before critical administrative functions to enforce access control. +- Be aware that `renounceOwnership` permanently removes ownership, disabling all owner-restricted functions. ## Integration Notes -The `OwnerTwoStepsMod` manages ownership state within specific storage slots. Facets that utilize this module should ensure they do not conflict with these storage positions. The `owner()`, `pendingOwner()`, `getOwnerStorage()`, and `getPendingOwnerStorage()` functions provide access to this state. Ownership checks are performed via the `requireOwner()` internal function. +The `OwnerTwoStepsMod` utilizes dedicated storage slots for `OwnerStorage` and `PendingOwnerStorage`. Facets can interact with these storage variables via the provided `getOwnerStorage` and `getPendingOwnerStorage` functions, which use inline assembly to access the correct storage locations. Any facet can call these functions to read ownership information. Ownership checks are performed by calling `requireOwner` on the `OwnerTwoStepsMod` facet.
- + diff --git a/website/docs/library/access/OwnerTwoSteps/index.mdx b/website/docs/library/access/OwnerTwoSteps/index.mdx index 525a9934..0b051554 100644 --- a/website/docs/library/access/OwnerTwoSteps/index.mdx +++ b/website/docs/library/access/OwnerTwoSteps/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; Two-step ownership transfer pattern. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/index.mdx b/website/docs/library/access/index.mdx index 28850d68..1e83a09d 100644 --- a/website/docs/library/access/index.mdx +++ b/website/docs/library/access/index.mdx @@ -11,4 +11,40 @@ import Icon from '@site/src/components/ui/Icon'; Access control patterns for permission management in Compose diamonds. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx index d0dd0b4e..b41ae979 100644 --- a/website/docs/library/diamond/DiamondCutFacet.mdx +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondCutFacet" -description: "Manage diamond facet additions, replacements, and removals." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondCutFacet.sol" +description: "Manage diamond facets and functions within a diamond proxy." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondCutFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage diamond facet additions, replacements, and removals. +Manage diamond facets and functions within a diamond proxy. -- Supports adding, replacing, and removing facets dynamically. -- Allows batching multiple facet operations in a single transaction. -- Optionally executes an initialization function after the cut. +- Supports adding, replacing, and removing functions and entire facets. +- Allows optional execution of an initialization function during a cut operation. +- Provides access to diamond storage and owner information through dedicated functions. ## Overview -The DiamondCutFacet enables the dynamic modification of a diamond's functionality by allowing the addition, replacement, or removal of facets. It provides a standardized interface for upgrading and extending the diamond's capabilities. +The DiamondCutFacet provides essential functionality for managing the diamond proxy's facets and functions. It allows for adding, replacing, and removing functions, as well as executing arbitrary code during the cut operation. This facet is crucial for upgrading and extending the diamond's capabilities. --- @@ -368,33 +368,29 @@ error InitializationFunctionReverted(address _initializationContractAddress, byt {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose/contracts/src/interfaces/IDiamondCut.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; -contract Deployer { - address public diamond; +contract DiamondCutConsumer { + IDiamondCut public diamondCutFacet; - function deployDiamond() public { - // ... deployment logic for diamond proxy and initial facets ... - diamond = address(0xYourDiamondAddress); + constructor(address _diamondCutFacetAddress) { + diamondCutFacet = IDiamondCut(_diamondCutFacetAddress); } - function upgradeDiamond() public { - IDiamondCut diamondCutFacet = IDiamondCut(diamond); - - // Example: Add a new facet - address newFacetAddress = address(0xNewFacetAddress); - bytes4[] memory selectorsToAdd = new bytes4[](2); - selectorsToAdd[0] = bytes4(keccak256(bytes('newFunction1()'))); - selectorsToAdd[1] = bytes4(keccak256(bytes('newFunction2()'))); + function addMyFacet(address _facetAddress, bytes4[] memory _functionSelectors) external { + // Assume DiamondLoupeFacet is already deployed and its selectors are known + // Assume MyFacet is deployed at _facetAddress + diamondCutFacet.diamondCut([(_facetAddress, IDiamondCut.FacetCutAction.Add, _functionSelectors)], address(0), ""); + } - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: newFacetAddress, - action: IDiamondCut.Action.ADD, - selectors: selectorsToAdd - }); + function replaceMyFacet(address _newFacetAddress, bytes4[] memory _functionSelectors) external { + // Assume MyFacet is deployed at _newFacetAddress + diamondCutFacet.diamondCut([(_newFacetAddress, IDiamondCut.FacetCutAction.Replace, _functionSelectors)], address(0), ""); + } - diamondCutFacet.diamondCut(cuts, address(0), ""); + function removeMyFacet(bytes4[] memory _functionSelectors) external { + // Remove functions associated with a facet + diamondCutFacet.diamondCut([], address(0), ""); // Placeholder for actual function removal logic } }`} @@ -402,19 +398,19 @@ contract Deployer { ## Best Practices -- Ensure the `diamondCut` function is called by an authorized address, typically the diamond owner or an admin contract. -- Carefully manage facet addresses and their associated selectors to avoid conflicts or unintended overwrites. -- When replacing facets, ensure the new facet's functions are compatible with existing diamond logic to maintain state integrity. +- Use `diamondCut` to atomically add, replace, or remove functions and facets. This ensures consistency and prevents partial upgrades. +- Always provide selectors when adding or replacing facets to ensure the diamond can route calls correctly. +- For upgrades, carefully manage the `InitializationFunctionReverted` error to ensure new facet initializers execute successfully. ## Security Considerations -The `diamondCut` function is highly sensitive and should only be callable by authorized entities. Incorrectly adding, replacing, or removing facets can lead to loss of functionality or unintended state changes. Ensure proper access control is implemented. Be cautious when replacing immutable functions or functions that have already been deployed. Reentrancy is not a concern as the function performs state changes before delegatecalls. +Access control is paramount; only authorized addresses (typically the diamond owner) should be able to call `diamondCut` to prevent unauthorized modifications to the diamond's functionality. Ensure that facet addresses provided are valid and that selectors do not conflict with existing functions, especially immutable ones. Reentrancy is mitigated by the atomic nature of the `diamondCut` operation.
- + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx index 194401aa..b7de9a9d 100644 --- a/website/docs/library/diamond/DiamondCutMod.mdx +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondCutMod" -description: "Manage diamond facets and functions on-chain." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondCutMod.sol" +description: "Manages facet additions, removals, and replacements on a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondCutMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage diamond facets and functions on-chain. +Manages facet additions, removals, and replacements on a diamond. -- Supports adding, replacing, and removing functions from diamond facets atomically. -- Allows optional execution of an initialization function via delegatecall after the diamond cut. -- Provides error handling for common issues like non-existent selectors or immutable function modifications. +- Dynamically adds, replaces, and removes functions from the diamond proxy's routing table. +- Supports batch operations for efficient interface updates. +- Allows for an optional initialization call via `delegatecall` during a cut operation. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The DiamondCutMod enables dynamic on-chain management of diamond facets. It allows adding, replacing, and removing functions, providing a flexible upgrade path for Compose diamonds. This module is critical for evolving diamond functionality without redeploying the entire proxy. +The DiamondCutMod provides essential functions for modifying the diamond's contract interface. It allows for the dynamic addition, replacement, or removal of functions across various facets. This module is crucial for maintaining and evolving the diamond's capabilities post-deployment, enabling upgrades and feature extensions. --- @@ -334,68 +334,47 @@ error RemoveFacetAddressMustBeZeroAddress(address _facet); {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose/diamond/contracts/modules/diamondCut/IDiamondCut.sol"; -import {FacetCut} from "@compose/diamond/contracts/modules/diamondCut/IDiamondCut.sol"; - -contract MyFacet { - // ... other facet functions ... - - function upgradeDiamond(address _diamondCutAddress, FacetCut[] memory _diamondCut) public { - IDiamondCut(_diamondCutAddress).diamondCut(_diamondCut, address(0), ""); +import {IDiamondCutMod} from "@compose/diamond-contracts/contracts/modules/diamondCut/IDiamondCutMod.sol"; +import {DiamondCutFacet} from "@compose/diamond-contracts/contracts/modules/diamondCut/DiamondCutFacet.sol"; + +contract MyDiamondConsumerFacet { + IDiamondCutMod constant DIAMOND_CUT_MODULE = IDiamondCutMod(address(this)); // Replace with actual diamond address + + function upgradeDiamond() external { + // Example: Add a new function + address newFacetAddress = address(0x123); // Address of the new facet contract + bytes4[] memory selectorsToAdd = new bytes4[](1); + selectorsToAdd[0] = DiamondCutFacet.addFunctions.selector; // Example selector + + DIAMOND_CUT_MODULE.diamondCut( + address(0), // Facet address for additions (can be zero if only replacing/removing) + selectorsToAdd, + new bytes4[](0), // Selectors to remove + new address[](0), // Facet addresses for replacements (can be empty) + new bytes4[][](0), // New selectors for replacements (can be empty) + address(0), // Target address for initialization call + bytes('') // Initialization calldata + ); } - - // Example of adding a new facet - function addMyNewFacet(address _diamondCutAddress, address _newFacetAddress, bytes4[] memory _selectors) public { - FacetCut[] memory cuts = new FacetCut[](1); - cuts[0] = FacetCut({ - facetAddress: _newFacetAddress, - action: 1, // 1 = ADD - selectors: _selectors - }); - IDiamondCut(_diamondCutAddress).diamondCut(cuts, address(0), ""); - } - - // Example of replacing a facet's function - function replaceMyFunction(address _diamondCutAddress, address _facetAddress, bytes4 _selector, address _newFacetAddress) public { - FacetCut[] memory cuts = new FacetCut[](1); - cuts[0] = FacetCut({ - facetAddress: _newFacetAddress, - action: 2, // 2 = REPLACE - selectors: new bytes4[](1) { _selector } - }); - IDiamondCut(_diamondCutAddress).diamondCut(cuts, address(0), ""); - } - - // Example of removing a facet's function - function removeMyFunction(address _diamondCutAddress, address _facetAddress, bytes4 _selector) public { - FacetCut[] memory cuts = new FacetCut[](1); - cuts[0] = FacetCut({ - facetAddress: address(0), // Address must be zero for removal - action: 3, // 3 = REMOVE - selectors: new bytes4[](1) { _selector } - }); - IDiamondCut(_diamondCutAddress).diamondCut(cuts, address(0), ""); - } -} -`} +}`} ## Best Practices -- Ensure the `diamondCut` function is called with appropriate access control, typically restricted to an owner or admin role. -- Carefully validate `facetAddress` and `selectors` to prevent accidental removal of critical functions or addition of malicious ones. -- Be aware of immutable functions; they cannot be removed or replaced. Use the `getStorage` function to inspect facet information if needed. +- Always ensure the facet address provided for adding functions is valid and contains the intended bytecode. Use `getStorage` to verify facet existence before removal. +- Be aware that `diamondCut` can execute an arbitrary `delegatecall` for initialization, requiring careful validation of the target address and calldata. +- Prefer adding or replacing functions over removing them to maintain backward compatibility where possible. Ensure immutable functions are not targeted for removal or replacement. ## Integration Notes -The DiamondCutMod interacts with the diamond's global function selector mapping. When functions are added, replaced, or removed, the DiamondCutMod updates this mapping. Facets interact with the diamond proxy, which routes calls based on this mapping. Immutable functions, identified during the cut, are permanently registered and cannot be altered via `diamondCut`. +This module directly interacts with the diamond's internal storage to manage the mapping of function selectors to facet addresses. Changes made via `diamondCut` are immediately reflected in the diamond proxy's dispatch logic. Facets should use `getStorage` to query the current facet implementations. Facet cuts are atomic; either all operations succeed or none do. The order of operations within a single `diamondCut` call is significant: additions are processed first, then replacements, and finally removals.
- + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx index ffcac34f..3215aa34 100644 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "DiamondLoupeFacet" description: "Inspect diamond facets, selectors, and storage." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondLoupeFacet.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondLoupeFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,13 +25,14 @@ Inspect diamond facets, selectors, and storage. -- Provides full visibility into diamond's facet and selector configuration. -- Optimized for gas efficiency when querying large numbers of selectors and facets. +- Provides read-only access to diamond's facet registry. +- Enables querying of facet addresses by function selector. +- Returns all registered facets and their associated function selectors. ## Overview -The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are registered, the function selectors they handle, and the addresses of these facets, facilitating debugging and external tooling integration. +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query which facets are registered, the function selectors they implement, and their associated addresses. This facet is crucial for understanding the diamond's internal structure and for building external tools that interact with it. --- @@ -209,25 +210,21 @@ Gets all facets and their selectors. Returns each unique facet address currently {`pragma solidity ^0.8.30; -import {IDiamondLoupe} from "@compose/diamond/facets/DiamondLoupe/IDiamondLoupe.sol"; +import {IDiamondLoupe} from "@compose/diamond-loupe/IDiamondLoupe.sol"; -contract ExampleUsage { - IDiamondLoupe public diamondLoupe; +contract DiamondLoupeConsumer { + IDiamondLoupe immutable diamondLoupeFacet; constructor(address _diamondAddress) { - diamondLoupe = IDiamondLoupe(_diamondAddress); + diamondLoupeFacet = IDiamondLoupe(_diamondAddress); } - function getAllFacets() public view returns (IDiamondLoupe.Facet[] memory) { - return diamondLoupe.facets(); + function getAllFacets() external view returns (IDiamondLoupe.Facet[] memory) { + return diamondLoupeFacet.facets(); } - function getFacetAddress(bytes4 _selector) public view returns (address) { - return diamondLoupe.facetAddress(_selector); - } - - function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { - return diamondLoupe.facetFunctionSelectors(_facet); + function getFacetAddress(bytes4 _selector) external view returns (address) { + return diamondLoupeFacet.facetAddress(_selector); } }`} @@ -236,17 +233,18 @@ contract ExampleUsage { - Integrate DiamondLoupeFacet into your diamond to enable runtime inspection of its components. -- Use the provided functions to verify facet registration and selector mappings during development and upgrades. +- Use the `facets()` function to retrieve a comprehensive list of registered facets and their selectors for auditing or integration purposes. +- Query `facetAddress(selector)` to determine which facet handles a specific function call, aiding in debugging and understanding execution flow. ## Security Considerations -This facet is read-only and does not pose direct security risks. However, relying on its output for critical access control logic in external contracts could be problematic if the diamond's state changes unexpectedly. Always ensure contracts interacting with the diamond's functions have appropriate authorization checks. +This facet is read-only and does not modify diamond state, posing no direct security risks. Its primary function is informational. Ensure the diamond's access control mechanisms are correctly implemented in other facets to protect sensitive operations.
- + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx index b22777a1..36b1a7d4 100644 --- a/website/docs/library/diamond/DiamondMod.mdx +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondMod" -description: "Manages diamond facets and provides core proxy logic." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/DiamondMod.sol" +description: "Internal functions and storage for diamond proxy." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages diamond facets and provides core proxy logic. +Internal functions and storage for diamond proxy. -- Supports adding multiple facets and their function selectors during initial diamond deployment. -- Acts as the diamond's central fallback for routing external calls to the correct facet implementation. -- Provides a mechanism (`getStorage`) to retrieve the facet address associated with a given function selector. +- Manages facet registration and function selector mapping within the diamond proxy. +- Provides the core `diamondFallback` mechanism for dynamic function dispatch to appropriate facets. +- Exposes an internal `getStorage` function for retrieving storage values (though direct use is discouraged in external facets). @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides essential functions for managing facets within a Compose diamond. It handles the addition of new facets during initial deployment and acts as the central fallback mechanism to route external calls to the appropriate facet implementation. Understanding its storage interactions is key for facet composition. +The DiamondMod module provides core internal logic for managing diamond facets and handling function calls. It is essential for the diamond proxy's operation, enabling facet registration and dynamic dispatch. --- @@ -195,29 +195,21 @@ error NoBytecodeAtAddress(address _contractAddress, string _message); {`pragma solidity ^0.8.30; -import {IDiamondMod} from "@compose/diamond-proxy/contracts/modules/DiamondMod.sol"; +import {IDiamondMod} from "@compose/contracts/src/diamond/IDiamondMod.sol"; contract MyFacet { - IDiamondMod public diamondMod; + IDiamondMod internal diamondMod; - function initialize(address _diamondMod) external { + constructor(address _diamondMod) { diamondMod = IDiamondMod(_diamondMod); } - /** - * @notice Calls a function on another facet via the DiamondMod. - */ - function callOtherFacet(bytes4 _functionSelector, address _facetAddress, bytes memory _calldata) external returns (bytes memory) { - // This is a simplified example. In a real scenario, the facet address would be dynamically resolved. - // The actual diamondFallback would handle this resolution internally. - return diamondMod.diamondFallback(_functionSelector, _facetAddress, _calldata); - } - - /** - * @notice Gets the storage slot for a specific function selector. - */ - function getFunctionStorage(bytes4 _functionSelector) external view returns (address) { - return diamondMod.getStorage(_functionSelector); + function someFunction() external { + // Example of calling a function that might interact with diamond storage + // Note: Direct calls to diamondMod internal functions are generally not exposed or recommended. + // This is illustrative; actual interaction would be through the diamond proxy itself. + // For instance, a facet might read storage values managed by the diamond proxy. + // bytes32 storageSlot = diamondMod.getStorageSlot(keccak256("myState")); } }`} @@ -225,19 +217,19 @@ contract MyFacet { ## Best Practices -- Facet functions should only be added during the initial diamond deployment phase via `addFacets`. Calls to `addFacets` after deployment are unsupported and will revert. -- Handle `FunctionNotFound` errors gracefully, as they indicate an attempt to call a non-existent function selector. -- Utilize `getStorage` to understand the storage layout and potential interactions between facets. +- Access control for functions like `addFacets` is crucial and should be managed externally, typically by an upgrade agent or owner. +- Ensure that `addFacets` is only called during the initial diamond deployment phase to maintain diamond immutability. +- Handle `FunctionNotFound` errors gracefully in consumer contracts that call functions routed through the diamond proxy. ## Integration Notes -The `DiamondMod` interacts directly with the diamond's storage to map function selectors to facet addresses. The `addFacets` function is designed to be called only once during deployment. The `diamondFallback` function is the core of the diamond proxy pattern, resolving function calls by looking up the selector in storage and then executing the corresponding facet. The `getStorage` function exposes this mapping for external inspection. +DiamondMod interacts directly with the diamond's storage layout to store facet information and function mappings. Facets added via `addFacets` are registered in a way that `diamondFallback` can resolve incoming calls. The `getStorage` function allows facets to access state managed by the diamond proxy, adhering to the storage slotting conventions. `addFacets` must be called during deployment; subsequent calls will revert.
- + diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx index ea84010d..847a57f6 100644 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ExampleDiamond" -description: "Example Diamond for Compose framework" -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/diamond/example/ExampleDiamond.sol" +description: "Diamond proxy for routing external calls to facets" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/example/ExampleDiamond.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Example Diamond for Compose framework +Diamond proxy for routing external calls to facets -- Manages facet registration and function selector mapping. -- Supports diamond cut operations for dynamic facet updates. -- Acts as a foundational example for Compose diamond deployments. +- Centralized function routing via delegatecall. +- Supports modularity by allowing dynamic addition/removal of functionality (facets). +- Enables upgradeability by replacing facet implementations without changing the diamond proxy address. ## Overview -The ExampleDiamond serves as a foundational contract within the Compose framework, demonstrating diamond proxy functionality. It manages facet registrations and routes calls to appropriate implementation contracts, showcasing the core composability principles. +The ExampleDiamond acts as a central proxy, managing function routing to various facets. It initializes the diamond by mapping function selectors to their corresponding facet addresses, enabling modular and upgradeable smart contract logic. --- @@ -85,26 +85,31 @@ Struct to hold facet address and its function selectors. struct FacetCut { {`pragma solidity ^0.8.30; -import {ExampleDiamond} from "@compose-protocol/diamond/contracts/ExampleDiamond.sol"; - -contract DeployExampleDiamond { - ExampleDiamond public diamond; - - function deploy() public { - // Assuming facet addresses and selectors are defined elsewhere - // Example: address facetAddress = address(new MyFacet()); - // Example: bytes4[] memory selectors = new bytes4[](1); - // selectors[0] = MyFacet.myFunction.selector; - // FacetCut[] memory facets = new FacetCut[](1); - // facets[0] = ExampleDiamond.FacetCut({ - // facetAddress: facetAddress, - // action: ExampleDiamond.FacetCutAction.Add, - // functionSelectors: selectors - // }); - - // In a real scenario, you would pass actual facet data - diamond = new ExampleDiamond(); - // diamond.diamondCut(facets, address(0), ""); // Example cut call +import {IDiamondCut} from "@compose/diamond/contracts/IDiamondCut.sol"; +import {IDiamondLoupe} from "@compose/diamond/contracts/IDiamondLoupe.sol"; + +// Assume ExampleDiamond is deployed and initialized +contract Consumer { + IDiamondCut internal immutable diamondCut; + IDiamondLoupe internal immutable diamondLoupe; + + constructor(address _diamondAddress) { + diamondCut = IDiamondCut(_diamondAddress); + diamondLoupe = IDiamondLoupe(_diamondAddress); + } + + function addFacet(address _facetAddress, bytes4[] memory _selectors) external { + // Call the diamond's cut function to add a new facet + // Requires owner permissions on the diamond + diamondCut.diamondCut([IDiamondCut.FacetCut({ + facetAddress: _facetAddress, + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: _selectors + })], address(0), ""); + } + + function getFacetAddress(bytes4 _selector) external view returns (address) { + return diamondLoupe.facetAddress(_selector); } }`} @@ -112,19 +117,19 @@ contract DeployExampleDiamond { ## Best Practices -- Use the `constructor` to initialize the diamond with initial facets and set the owner. -- Leverage `diamondCut` for adding, replacing, or removing facets post-deployment. -- Ensure facet addresses and their associated function selectors are correctly managed during cuts. +- Initialize the diamond with owner and initial facets during deployment. +- Use the `diamondCut` interface for adding, replacing, or removing facets. +- Grant necessary permissions to the entity responsible for managing facet additions/removals. ## Security Considerations -Access control for diamond cut operations should be strictly managed to prevent unauthorized facet modifications. Ensure that facet addresses provided during initialization or cuts are trusted implementations. +Ensure that only authorized addresses can call `diamondCut` to prevent unauthorized facet manipulation. Input validation for facet addresses and selectors is crucial. Be mindful of state coupling between facets that might share storage slots.
- + diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx index 8d62a9ae..ad56c9b3 100644 --- a/website/docs/library/diamond/example/index.mdx +++ b/website/docs/library/diamond/example/index.mdx @@ -11,4 +11,12 @@ import Icon from '@site/src/components/ui/Icon'; example components for Compose diamonds. -_No items in this category yet._ + + } + size="medium" + /> + diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx index b11c7a6f..0ef75825 100644 --- a/website/docs/library/diamond/index.mdx +++ b/website/docs/library/diamond/index.mdx @@ -11,4 +11,40 @@ import Icon from '@site/src/components/ui/Icon'; Core diamond proxy functionality for ERC-2535 diamonds. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx index 4f169ade..7e19001e 100644 --- a/website/docs/library/index.mdx +++ b/website/docs/library/index.mdx @@ -11,4 +11,40 @@ import Icon from '@site/src/components/ui/Icon'; API reference for all Compose modules and facets. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx index 11cc600b..ad461096 100644 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC165Mod" -description: "ERC-165 interface detection and registration." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/interfaceDetection/ERC165/ERC165Mod.sol" +description: "Implement ERC-165 interface detection." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/interfaceDetection/ERC165/ERC165Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -ERC-165 interface detection and registration. +Implement ERC-165 interface detection. -- Standardized ERC-165 interface detection. -- Allows facets to declare supported interfaces at runtime. -- Utilizes a dedicated storage slot for interface support. +- Implements the ERC-165 standard for interface detection. +- Provides a dedicated storage slot for interface support data. +- Requires explicit registration of supported interfaces. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC165Mod provides a standardized way for facets to implement and report ERC-165 interface support. By registering interfaces during facet initialization, other contracts can reliably query for supported functionality through the diamond proxy. +The ERC165Mod provides the necessary storage and internal functions to implement the ERC-165 standard for interface detection. This allows other contracts to query which interfaces a diamond proxy supports, enhancing composability and interoperability. --- @@ -116,37 +116,50 @@ Register that a contract supports an interface Call this function during initial {`pragma solidity ^0.8.30; -import {LibERC165} from "@compose/modules/erc165/LibERC165.sol"; -import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; +import {ERC165Mod} from "@compose/modules/erc165/ERC165Mod.sol"; -contract MyERC721Facet { - LibERC165.ERC165Storage internal _erc165Storage; +contract MyFacet { + ERC165Mod.Storage private _erc165Storage; - function initialize(address _diamondProxy) external { - // Register ERC721 interface support - LibERC165.registerInterface(address(this), type(IERC721).interfaceId); + /** + * @notice Initializes the ERC165 module and registers the ERC721 interface. + */ + function initialize() external { + // Bind storage to the correct slot + ERC165Mod.bindStorage(address(this), _erc165Storage); + + // Register the interface ID for the IERC721 interface + ERC165Mod.registerInterface(_erc165Storage, type(IERC721).interfaceId); } - // Other ERC721 facet functions... + /** + * @notice Queries if the diamond supports a given interface ID. + * @param _interfaceId The interface ID to query. + * @return bool True if the interface is supported, false otherwise. + */ + function supportsInterface(bytes4 _interfaceId) external view returns (bool) { + // Delegate to the ERC165 module's internal function + return ERC165Mod.supportsInterface(_erc165Storage, _interfaceId); + } }`} ## Best Practices -- Register all supported interfaces during facet initialization. -- Ensure the `ERC165Storage` struct is correctly included in the diamond's storage layout. -- Avoid calling `registerInterface` for interfaces not actually implemented by the facet. +- Call `ERC165Mod.bindStorage` during facet initialization to correctly map the module's storage. +- Register all supported interface IDs during initialization using `ERC165Mod.registerInterface`. +- Implement `supportsInterface` in your facet to delegate to `ERC165Mod.supportsInterface`. ## Integration Notes -The ERC165Mod relies on a dedicated storage slot for its `ERC165Storage` struct. Facets that implement ERC-165 must include this struct in their own storage layout and call `LibERC165.registerInterface` during their initialization. The `getStorage` function uses inline assembly to bind to the correct storage position, ensuring interface detection works correctly through the diamond proxy. +The ERC165Mod utilizes its own storage slot to maintain a mapping of supported interface IDs. Facets must explicitly bind to this storage using `ERC165Mod.bindStorage` during their initialization phase. The `supportsInterface` function within the ERC165 module checks this internal mapping. Any facet that needs to declare support for an interface must call `ERC165Mod.registerInterface` during its initialization, ensuring that the interface ID is added to the module's registry before the diamond is made operational.
- + diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx index fcd9a680..26ecf715 100644 --- a/website/docs/library/interfaceDetection/ERC165/index.mdx +++ b/website/docs/library/interfaceDetection/ERC165/index.mdx @@ -11,4 +11,12 @@ import Icon from '@site/src/components/ui/Icon'; ERC-165 components for Compose diamonds. -_No items in this category yet._ + + } + size="medium" + /> + diff --git a/website/docs/library/interfaceDetection/index.mdx b/website/docs/library/interfaceDetection/index.mdx index 0e77e722..65448bd8 100644 --- a/website/docs/library/interfaceDetection/index.mdx +++ b/website/docs/library/interfaceDetection/index.mdx @@ -11,4 +11,12 @@ import Icon from '@site/src/components/ui/Icon'; ERC-165 interface detection support. -_No items in this category yet._ + + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx index 7131c540..04b22f0c 100644 --- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC1155Facet" -description: "Manages ERC-1155 fungible and non-fungible tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC1155/ERC1155Facet.sol" +description: "Manage ERC-1155 fungible and non-fungible tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC1155/ERC1155Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-1155 fungible and non-fungible tokens. +Manage ERC-1155 fungible and non-fungible tokens. -- Supports both fungible and non-fungible token types within a single contract. -- Implements batched transfer and balance checking for efficiency. -- Handles operator approvals for delegated token management. -- Provides flexible URI resolution for token metadata. +- Supports both fungible and non-fungible ERC-1155 tokens. +- Provides standard `balanceOf`, `balanceOfBatch`, `safeTransferFrom`, and `safeBatchTransferFrom` functions. +- Includes `setApprovalForAll` and `isApprovedForAll` for operator permissions. +- Allows for dynamic token URIs using a base URI and individual token URI overrides. ## Overview -The ERC1155Facet provides a comprehensive implementation of the ERC-1155 Multi-Token Standard. It enables a Compose diamond to manage multiple token types, supporting both fungible and non-fungible assets. This facet handles token transfers, batch operations, approvals, and URI resolution, making it a core component for any diamond requiring flexible token management. +The ERC1155Facet enables a Compose diamond to manage ERC-1155 compliant tokens. It provides standard functions for token transfers, batch transfers, balance checks, and operator approvals, facilitating the creation and management of multi-token standards within a single diamond. --- @@ -624,51 +624,50 @@ error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); {`pragma solidity ^0.8.30; -import {IERC1155Errors, IERC1155Facet} from "@compose/diamond/facets/ERC1155/IERC1155Facet.sol"; -import {DiamondProxy, IDiamondProxy} from "@compose/diamond/DiamondProxy.sol"; +import {IERC1155Facet} from "@compose-protocol/diamond-contracts/contracts/facets/erc1155/IERC1155Facet.sol"; +import {ERC1155Facet} from "@compose-protocol/diamond-contracts/contracts/facets/erc1155/ERC1155Facet.sol"; -contract ERC1155Consumer { - // Assume diamondProxy is already deployed and initialized - IDiamondProxy public diamondProxy; +contract MyDiamond is IERC1155Facet { + // ... other facets and diamond setup ... - constructor(address _diamondProxyAddress) { - diamondProxy = IDiamondProxy(_diamondProxyAddress); + function supportsInterface(bytes4 _interfaceId) external view virtual override returns (bool) { + // ... other interface checks ... + if (_interfaceId == type(IERC1155Facet).interfaceId) { + return true; + } + return false; } - // Example: Get balance of a specific token for an account - function getMyTokenBalance(uint256 _tokenId, address _account) external view returns (uint256) { - bytes4 selector = IERC1155Facet.balanceOf.selector; - (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector, _tokenId, _account)); - require(success, "ERC1155Consumer: balanceOf call failed"); - return abi.decode(data, (uint256)); + // Example: Transferring tokens + function transferERC1155(address _to, uint256 _id, uint256 _value) external { + // Assume caller is approved or owner + safeTransferFrom(_to, msg.sender, _id, _value, ""); } - // Example: Get the URI for a token - function getTokenUri(uint256 _tokenId) external view returns (string memory) { - bytes4 selector = IERC1155Facet.uri.selector; - (bool success, bytes memory data) = address(diamondProxy).call(abi.encodeWithSelector(selector, _tokenId)); - require(success, "ERC1155Consumer: uri call failed"); - return abi.decode(data, (string)); + // Example: Checking balance + function getERC1155Balance(address _account, uint256 _id) external view returns (uint256) { + return balanceOf(_account, _id); } -}`} +} +`} ## Best Practices -- Ensure the ERC1155Facet is correctly initialized with appropriate base URIs or token-specific URIs if needed. -- Implement robust access control within your diamond's governance or access control facets to manage who can call administrative functions on ERC1155Facet, such as setting approvals. -- When designing new token types, consider the `uri` function's behavior regarding base URIs and token-specific URIs for optimal metadata management. +- Initialize the `ERC1155Facet` with `baseURI` and `tokenURIs` as needed during diamond deployment. +- Ensure appropriate access control is implemented on functions that modify token balances or approvals, if required by your diamond's logic. +- Store the `ERC1155Facet` contract address in the diamond's facet registry. ## Security Considerations -The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks to prevent unsafe transfers to non-contract addresses or contract addresses that do not implement the `onERC1155Received` or `onERC1155BatchReceived` hooks. Ensure that any contracts interacting with this facet are audited for reentrancy if they implement these hooks. Access control for `setApprovalForAll` should be managed carefully to prevent unauthorized token delegation. +The `ERC1155Facet` itself does not enforce ownership or complex access controls beyond what is standard for ERC-1155. Ensure that the diamond's upgradeability mechanism and any custom logic interacting with this facet correctly manage permissions and prevent unauthorized token transfers or approvals. Input validation for token IDs and amounts is handled by the facet to prevent basic errors.
- + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx index c82aeaa4..16268088 100644 --- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "ERC1155Mod" description: "Manages ERC-1155 token balances, transfers, and metadata." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC1155/ERC1155Mod.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC1155/ERC1155Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,9 +25,9 @@ Manages ERC-1155 token balances, transfers, and metadata. -- Supports both single and batch operations for minting, burning, and transferring tokens. -- Implements safe transfer mechanisms, including `IERC1155Receiver` callback validation. -- Provides functionality to set and retrieve base URIs and token-specific URIs for metadata. +- Supports minting and burning of single and batch token types. +- Implements safe transfer logic compliant with EIP-1155. +- Allows setting base and token-specific URIs for metadata. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC1155Mod facet implements the core logic for managing ERC-1155 fungible and non-fungible tokens. It provides functionalities for minting, burning, transferring tokens safely, and setting token URIs. This module ensures proper balance tracking and adherence to ERC-1155 standards, enabling composable token management within a diamond. +This module implements the core ERC-1155 token functionality, including minting, burning, and safe transfers. It ensures proper balance tracking and adherence to the ERC-1155 standard, enabling composable multi-token systems within a diamond. --- @@ -561,24 +561,24 @@ error ERC1155MissingApprovalForAll(address _operator, address _owner); {`pragma solidity ^0.8.30; -import {IERC1155Mod} from "@compose/modules/erc1155/ERC1155Mod.sol"; +import {IERC1155Mod} from "@compose/core/src/modules/ERC1155Mod.sol"; -contract MyDiamondFacet { - IERC1155Mod internal immutable erc1155Mod; +contract MyFacet { + IERC1155Mod public immutable erc1155Mod; - constructor(address _diamondAddress) { - erc1155Mod = IERC1155Mod(_diamondAddress); + constructor(address diamondAddress) { + // Assuming ERC1155Mod facet is already deployed and accessible + erc1155Mod = IERC1155Mod(diamondAddress); } - /** - * @notice Mints 10 units of token ID 1 to address(1) and safely transfers 5 units of token ID 2 from msg.sender to address(1). - */ - function manageErc1155Tokens() external { - // Mint tokens - erc1155Mod.mint(address(1), 1, 10); + function mintErc1155Tokens(address to, uint256 id, uint256 amount) external { + // Call the mint function from the ERC1155Mod facet + erc1155Mod.mint(to, id, amount); + } - // Safely transfer tokens - erc1155Mod.safeTransferFrom(msg.sender, address(1), 2, 5); + function transferErc1155Tokens(address from, address to, uint256 id, uint256 amount) external { + // Call the safeTransferFrom function from the ERC1155Mod facet + erc1155Mod.safeTransferFrom(from, to, id, amount, ""); } }`} @@ -586,19 +586,19 @@ contract MyDiamondFacet { ## Best Practices -- Ensure proper access control is implemented for minting and burning functions if they are intended to be restricted. -- Always validate that the recipient contract implements the `IERC1155Receiver` interface when performing safe transfers to contracts. -- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, `ERC1155InvalidReceiver`, `ERC1155InvalidSender`, and `ERC1155MissingApprovalForAll` errors appropriately in your calling facets. +- Always validate receiver addresses when minting or transferring to contracts to ensure they implement the ERC1155Receiver interface. +- Use `safeTransferFrom` and `safeBatchTransferFrom` for transfers involving contract recipients to ensure proper handling of tokens. +- Handle potential `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, `ERC1155InvalidReceiver`, `ERC1155InvalidSender`, and `ERC1155MissingApprovalForAll` errors gracefully. ## Integration Notes -This module interacts with the diamond's storage to manage ERC-1155 token balances and URIs. The `getStorage` function provides direct access to the ERC-1155 storage struct, allowing other facets to read or modify state directly if necessary, though direct modification should be done with extreme caution to maintain invariants. The ERC-1155 storage struct is expected to be located at a predefined diamond storage slot. Any changes to this storage layout require careful consideration to maintain backward compatibility for existing facets. +This module interacts with the diamond's storage to manage ERC-1155 balances and token URIs. The storage is accessed via a predefined slot. Facets interacting with this module should be aware of the ERC-1155 storage layout and potential state changes.
- + diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx index 445e1f0e..2ce0d206 100644 --- a/website/docs/library/token/ERC1155/index.mdx +++ b/website/docs/library/token/ERC1155/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; ERC-1155 multi-token implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx index 1f477e0e..91a21928 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BurnFacet" -description: "Burn ERC-20 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +description: "Burn ERC20 tokens from caller or spender allowance." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20/ERC20BurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,17 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC-20 tokens within a Compose diamond. +Burn ERC20 tokens from caller or spender allowance. -- Allows burning of ERC-20 tokens directly via the diamond proxy. -- Supports burning from the caller's balance (`burn`). -- Supports burning from an allowance granted by another account (`burnFrom`). +- Supports burning tokens from the caller's balance. +- Enables burning tokens from another account's balance using allowances. ## Overview -The ERC20BurnFacet enables the burning of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account's allowance, facilitating token destruction mechanisms. +The ERC20BurnFacet allows for the destruction of ERC20 tokens within a Compose diamond. It provides functions to burn tokens directly from the caller's balance or by deducting from an allowance granted by another account. This facet facilitates token supply reduction mechanisms. --- @@ -208,22 +207,21 @@ error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _ {`pragma solidity ^0.8.30; -import {IERC20BurnFacet} from "@compose/contracts/facets/erc20/IERC20BurnFacet.sol"; +import {IERC20BurnFacet} from "@compose/diamond/facets/ERC20BurnFacet.sol"; -contract ERC20BurnConsumer { - // Replace with the actual diamond proxy address - address immutable DIAMOND_PROXY; +contract BurnExample { + address immutable diamondAddress; - constructor(address _diamondProxy) { - DIAMOND_PROXY = _diamondProxy; + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; } - function burnMyTokens(address _tokenAddress, uint256 _amount) external { - IERC20BurnFacet(DIAMOND_PROXY).burn(_tokenAddress, _amount); + function burnTokens(uint256 _amount) public { + IERC20BurnFacet(diamondAddress).burn(_amount); } - function burnOtherTokens(address _tokenAddress, address _from, uint256 _amount) external { - IERC20BurnFacet(DIAMOND_PROXY).burnFrom(_tokenAddress, _from, _amount); + function burnTokensFromSpender(address _from, uint256 _amount) public { + IERC20BurnFacet(diamondAddress).burnFrom(_from, _amount); } }`} @@ -231,18 +229,18 @@ contract ERC20BurnConsumer { ## Best Practices -- Ensure the ERC20BurnFacet is correctly added to the diamond proxy during deployment or upgrade. -- Use `burnFrom` only when the caller has a sufficient allowance for the specified token and amount. +- Ensure the ERC20 token contract is correctly deployed and accessible via the diamond proxy. +- Use `burnFrom` only after verifying sufficient allowance has been set for the spender. ## Security Considerations -The `burn` function relies on the caller's balance. The `burnFrom` function relies on the caller's allowance. Ensure sufficient balances and allowances are managed correctly to prevent `ERC20InsufficientBalance` or `ERC20InsufficientAllowance` errors. No reentrancy concerns as the facet does not make external calls. +The `burn` and `burnFrom` functions reduce the total supply of ERC20 tokens. Ensure that the logic triggering these burns aligns with the intended tokenomics. `burnFrom` requires that the caller has been granted sufficient allowance by the `_from` address prior to invocation. The facet relies on the underlying ERC20 token implementation for balance and allowance checks, and emits `Transfer` events to the zero address as per ERC20 standards for burning.
- + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx index 57e1097f..12395769 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20Facet" -description: "Standard ERC-20 token functionality for Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20/ERC20Facet.sol" +description: "Implements the ERC20 token standard for Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20/ERC20Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Standard ERC-20 token functionality for Compose diamonds. +Implements the ERC20 token standard for Compose diamonds. -- Implements the full ERC-20 token standard interface. -- Manages token supply, balances, and allowances directly within the diamond's storage. -- Supports standard ERC-20 events (`Transfer`, `Approval`) for off-chain tracking. +- Full ERC20 standard compliance. +- Manages token state via the diamond's storage pattern. +- Supports standard token operations: name, symbol, decimals, totalSupply, balanceOf, allowance, approve, transfer, transferFrom. ## Overview -This facet provides a complete implementation of the ERC-20 token standard, enabling tokens to be managed and interacted with within a Compose diamond. It exposes core functions for querying token metadata, managing balances, and handling approvals and transfers, ensuring interoperability with the broader Ethereum ecosystem. +The ERC20Facet provides standard ERC20 token functionality, including balance tracking, transfers, and allowances. It integrates with the diamond's storage pattern to manage token state and adheres to the ERC20 specification for broad compatibility. --- @@ -522,52 +522,50 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20Facet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {IERC20Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20/IERC20Facet.sol"; +import {DiamondProxy} from "@compose-protocol/diamond-contracts/contracts/DiamondProxy.sol"; -contract ERC20Deployment { - address internal diamondAddress; +contract ERC20User { + IERC20Facet public erc20Facet; - function deploy(address _diamondAddress) external { - diamondAddress = _diamondAddress; + constructor(address _diamondProxyAddress) { + erc20Facet = IERC20Facet(_diamondProxyAddress); } - function addERC20Facet(bytes4[] memory selectors) external { - // Assuming ERC20Facet is already deployed and its address is known - address erc20FacetAddress = address(0x1234567890123456789012345678901234567890); // Replace with actual ERC20Facet address - - IDiamondCut(diamondAddress).diamondCut([ - IDiamondCut.FacetCut({ - facetAddress: erc20FacetAddress, - action: IDiamondCut.FacetCutAction.ADD, - selectors: selectors // e.g., ERC20Facet.name.selector, ERC20Facet.symbol.selector, etc. - }) - ], address(0), ""); + function getTokenName() public view returns (string memory) { + return erc20Facet.name(); } - function transferTokens(address _to, uint256 _amount) external { - IERC20Facet(diamondAddress).transfer(_to, _amount); + function getTokenSymbol() public view returns (string memory) { + return erc20Facet.symbol(); } -} -`} + + function getTokenBalance(address _account) public view returns (uint256) { + return erc20Facet.balanceOf(_account); + } + + function approveSpend(address _spender, uint256 _amount) public { + erc20Facet.approve(_spender, _amount); + } +}`} ## Best Practices -- Initialize token metadata (name, symbol, decimals) during diamond deployment, typically within a separate initialization facet or a dedicated deployment script. -- Ensure the `approve` function is used correctly to grant spending allowances before invoking `transferFrom` to prevent unintended token movements. -- Leverage diamond upgradeability to replace or add ERC-20 functionality without altering the diamond's core address. +- Initialize the ERC20Facet with appropriate token name, symbol, and decimals during diamond deployment. +- Ensure the `approve` function is used before calling `transferFrom` to prevent unexpected token movements. +- Manage access control for administrative functions (if any) outside this facet, as standard ERC20 operations are permissionless. ## Security Considerations -Input validation is crucial for all transfer and approval functions to prevent integer overflows and underflows. Ensure sender and receiver addresses are valid. Allowance checks must be strictly enforced in `transferFrom` to prevent unauthorized spending. Reentrancy is not a concern as state changes occur before external calls (which are not present in this facet). +Standard ERC20 vulnerabilities apply. Ensure sufficient allowances are set before using `transferFrom`. Reentrancy is mitigated by the diamond proxy's architecture. Input validation is handled within the facet functions to prevent invalid operations.
- + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx index ffd15274..88421578 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20Mod" -description: "ERC-20 token logic with mint, burn, transfer, and approval functions." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20/ERC20Mod.sol" +description: "ERC-20 token logic with standard functions and storage." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20/ERC20Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -ERC-20 token logic with mint, burn, transfer, and approval functions. +ERC-20 token logic with standard functions and storage. -- Supports standard ERC-20 operations: mint, burn, transfer, and approve. -- Defines a fixed storage slot for ERC-20 state, promoting composability. -- Utilizes custom errors for clear and gas-efficient error reporting. +- Implements standard ERC-20 `transfer`, `transferFrom`, `approve`, `mint`, and `burn` operations. +- Provides a deterministic storage layout via `getStorage`, compatible with diamond storage patterns. +- Defines custom errors for common ERC-20 failure conditions, improving gas efficiency and clarity. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC20Mod provides essential ERC-20 token functionalities including minting, burning, transfers, and approvals. It defines a standard storage layout for ERC-20 state, allowing facets to compose this logic into a diamond proxy. This enables a single diamond to manage multiple ERC-20 tokens or integrate ERC-20 functionality alongside other features. +The ERC20Mod provides essential ERC-20 token functionality, including minting, burning, transfers, and approvals. It defines a standard storage layout for ERC-20 state, enabling modular integration into Compose diamonds. This allows diamonds to manage ERC-20 tokens efficiently and composably. --- @@ -378,25 +378,28 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Mod } from "./IERC20Mod.sol"; +import {IERC20Mod } from "./interfaces/IERC20Mod.sol"; +import { ERC20Mod } from "./ERC20Mod.sol"; -contract MyTokenFacet { - IERC20Mod private immutable _erc20Mod; +contract MyERC20Facet { + // This facet would be part of a diamond proxy. + // The diamond's storage layout defines the ERC20Mod's storage slot. - constructor(address _diamondProxy) { - _erc20Mod = IERC20Mod(_diamondProxy); + function transferTokens(address to, uint256 amount) external { + // Assume diamond storage is correctly initialized for ERC20Mod. + // Call the internal function via the diamond proxy mechanism. + IERC20Mod(address(this)).transfer(to, amount); } - function mintTokens(address _to, uint256 _amount) external { - _erc20Mod.mint(_to, _amount); + function approveSpending(address spender, uint256 amount) external { + IERC20Mod(address(this)).approve(spender, amount); } - function transferTokens(address _to, uint256 _amount) external { - _erc20Mod.transfer(msg.sender, _to, _amount); - } - - function approveSpender(address _spender, uint256 _amount) external { - _erc20Mod.approve(msg.sender, _spender, _amount); + function mintTokens(address to, uint256 amount) external { + // This would likely require specific access control mechanisms + // managed by the diamond's access control facet. + // For demonstration, assuming it's callable. + IERC20Mod(address(this)).mint(to, amount); } }`} @@ -404,19 +407,19 @@ contract MyTokenFacet { ## Best Practices -- Ensure the ERC20Mod facet is correctly initialized with the diamond proxy address. -- Handle `ERC20InsufficientAllowance`, `ERC20InsufficientBalance`, and `ERC20InvalidReceiver` errors to manage token operations robustly. -- When upgrading or modifying facets, be aware of the ERC-20 storage layout to maintain data integrity. +- Ensure the ERC20Mod's storage slot is correctly initialized before deploying facets that interact with it. +- Implement robust access control within your diamond for sensitive functions like `mint` and `burn`. +- Handle custom errors like `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` gracefully in your facets. ## Integration Notes -The ERC20Mod utilizes a specific storage slot to hold its `ERC20Storage` struct. Facets interacting with this module should call `getStorage()` using inline assembly to obtain a pointer to this struct, ensuring correct access to balances, allowances, and total supply. Any changes to the `ERC20Storage` struct, such as adding new trailing variables, must be carefully managed to avoid breaking existing facets. The order of variables within the struct must be preserved. +The ERC20Mod relies on the diamond's storage pattern. The `getStorage` function uses inline assembly to bind to a fixed storage slot. Facets interacting with ERC20Mod will call its functions through the diamond proxy. The storage layout for ERC20Mod is defined within the module itself and should not be altered by other facets to maintain compatibility. Any changes to the ERC20Mod storage struct must be carefully managed during diamond upgrades to preserve state.
- + diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx index f7559880..00ba6f43 100644 --- a/website/docs/library/token/ERC20/ERC20/index.mdx +++ b/website/docs/library/token/ERC20/ERC20/index.mdx @@ -11,4 +11,26 @@ import Icon from '@site/src/components/ui/Icon'; ERC-20 fungible token implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx index ecd4687b..81a6348c 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain token bridging for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +description: "Facilitates cross-chain ERC20 token bridging operations." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Facilitates cross-chain token bridging for ERC-20 tokens. +Facilitates cross-chain ERC20 token bridging operations. -- Enables cross-chain minting and burning of ERC-20 tokens. -- Enforces `trusted-bridge` role for all cross-chain operations. -- Provides internal access control checks for bridge trustworthiness. +- Role-based access control for cross-chain operations, requiring the `trusted-bridge` role. +- Explicit functions for cross-chain minting and burning of ERC20 tokens. +- Utilizes inline assembly for efficient retrieval of storage structs. ## Overview -The ERC20BridgeableFacet enables secure and controlled cross-chain minting and burning operations for ERC-20 tokens within a Compose diamond. It ensures that only trusted bridge addresses can initiate these operations, maintaining the integrity of token balances across different networks. +The ERC20BridgeableFacet enables secure and controlled cross-chain transfers of ERC20 tokens within a Compose diamond. It provides functions for minting and burning tokens on behalf of trusted bridge operators, ensuring integrity and proper role-based access. --- @@ -372,43 +372,33 @@ error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _ {`pragma solidity ^0.8.30; -import {IERC20BridgeableFacet} from "../facets/ERC20BridgeableFacet.sol"; -import {IDiamondCut} from "../diamond/interfaces/IDiamondCut.sol"; - -contract DeployERC20BridgeableFacet { - address constant ERC20_BRIDGEABLE_FACET_IMPL = address(0xYourERC20BridgeableFacetImplementationAddress); - - // Function to add the ERC20BridgeableFacet to a diamond - function addERC20BridgeableFacet(address _diamondAddress) external { - bytes[] memory selectors = new bytes[](5); - selectors[0] = IERC20BridgeableFacet.getERC20Storage.selector; - selectors[1] = IERC20BridgeableFacet.getAccessControlStorage.selector; - selectors[2] = IERC20BridgeableFacet.crosschainMint.selector; - selectors[3] = IERC20BridgeableFacet.crosschainBurn.selector; - selectors[4] = IERC20BridgeableFacet.checkTokenBridge.selector; - - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: ERC20_BRIDGEABLE_FACET_IMPL, - action: IDiamondCut.FacetCutAction.Add, - selectors: selectors - }); - - // Assume _diamondAddress is the address of your diamond proxy - // The diamond cut function is typically called on the diamond itself - // For example: IDiamondCut(_diamondAddress).diamondCut(cut, address(0), ""); - } +import {IERC20BridgeableFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; +import {Diamond} from "@compose-protocol/diamond-contracts/contracts/Diamond.sol"; - // Example of calling crosschainMint (requires trusted-bridge role) - function mintForBridge(address _diamondAddress, address _to, uint256 _amount) external { - // Assume _diamondAddress is the address of your diamond proxy - IERC20BridgeableFacet(_diamondAddress).crosschainMint(_to, _amount); - } +contract Deployer { + function deploy() external { + // Assume diamond is already deployed and initialized + Diamond diamond = Diamond(address(0x123)); + + // Get the ERC20BridgeableFacet interface + IERC20BridgeableFacet erc20BridgeableFacet = IERC20BridgeableFacet(address(diamond)); + + // Example: Mint tokens across chains (called by a trusted bridge operator) + address tokenAddress = address(0xabc); + address recipient = address(0xdef); + uint256 amount = 100 ether; + bytes32 metadata = "0x"; + + // Assuming the caller has the 'trusted-bridge' role + erc20BridgeableFacet.crosschainMint(tokenAddress, recipient, amount, metadata); + + // Example: Burn tokens across chains (called by a trusted bridge operator) + address sender = address(0xghi); + uint256 burnAmount = 50 ether; + bytes32 burnMetadata = "0x"; - // Example of calling crosschainBurn (requires trusted-bridge role) - function burnForBridge(address _diamondAddress, address _from, uint256 _amount) external { - // Assume _diamondAddress is the address of your diamondส์ proxy - IERC20BridgeableFacet(_diamondAddress).crosschainBurn(_from, _amount); + // Assuming the caller has the 'trusted-bridge' role + erc20BridgeableFacet.crosschainBurn(tokenAddress, sender, burnAmount, burnMetadata); } }`} @@ -416,19 +406,19 @@ contract DeployERC20BridgeableFacet { ## Best Practices -- Ensure the `trusted-bridge` role is assigned only to verified and secure bridge addresses to prevent unauthorized cross-chain operations. -- Utilize the `checkTokenBridge` internal function or its logic within external calls to enforce access control before executing mint or burn operations. -- Store and manage diamond storage slots correctly when integrating this facet to avoid state corruption. +- Ensure the `trusted-bridge` role is correctly assigned to authorized bridge operator addresses via the Access Control facet. +- Store the ERC20 token addresses that will be bridged in a secure and auditable manner, likely managed by a separate facet or contract. +- Verify that the diamond proxy has the ERC20BridgeableFacet correctly appended and selectors are mapped. ## Security Considerations -Access to `crosschainMint` and `crosschainBurn` is restricted to addresses with the `trusted-bridge` role. The `checkTokenBridge` function verifies this role, preventing unauthorized cross-chain token transfers. Ensure the role management system for `trusted-bridge` is robust and secure to prevent malicious actors from gaining control. Reentrancy is not a direct concern for mint/burn functions themselves, but dependent external calls should be carefully audited. +This facet is callable by addresses with the `trusted-bridge` role. Ensure this role is granted with extreme caution and only to verified, audited bridge operator contracts or addresses. Input validation for token addresses, amounts, and recipient/sender addresses is critical to prevent unexpected state changes or token loss. Reentrancy is not directly applicable as functions do not make external calls that return control to the caller within the same execution context, but external calls to the bridged ERC20 token contract should be considered in the overall security model.
- + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx index 71eccdae..e4a1e49b 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BridgeableMod" -description: "Manage cross-chain ERC20 token transfers and burns." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +description: "Manages cross-chain ERC20 token transfers and minting." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage cross-chain ERC20 token transfers and burns. +Manages cross-chain ERC20 token transfers and minting. -- Enables cross-chain minting and burning of ERC20 tokens. -- Enforces strict access control via a `trusted-bridge` role. -- Provides utility functions to retrieve module storage pointers. +- Enables secure cross-chain minting and burning of ERC20 tokens. +- Enforces access control, restricting operations to addresses with the `trusted-bridge` role. +- Provides helper functions to access internal storage for auditing and debugging. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC20Bridgeable module provides functionality for minting and burning ERC20 tokens across different chains. It enforces access control to ensure only trusted bridge operators can perform these sensitive operations, enhancing security for cross-chain token management. +The ERC20Bridgeable module enables secure cross-chain operations for ERC20 tokens. It provides functions for minting and burning tokens on behalf of other chains, with strict access control to ensure only trusted bridge operators can perform these actions. This module is crucial for maintaining the integrity of token balances across distributed ledger environments. --- @@ -380,38 +380,19 @@ error ERC20InvalidSender(address _sender); {`pragma solidity ^0.8.30; -import {IERC20BridgeableMod} from "../modules/ERC20BridgeableMod.sol"; -import {DiamondStorage} from "../facets/DiamondStorage.sol"; +import {IERC20BridgeableFacet} from "@compose/contracts/src/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; -contract ERC20BridgeableFacet { - using DiamondStorage for DiamondStorage; +contract MyFacet { + IERC20BridgeableFacet internal constant ERC20_BRIDGEABLE = IERC20BridgeableFacet(address(this)); - IERC20BridgeableMod private _erc20BridgeableMod; - - function setERC20BridgeableMod(address _addr) external { - _erc20BridgeableMod = IERC20BridgeableMod(_addr); - } - - /** - * @notice Mints ERC20 tokens on a different chain. - * @dev Requires the caller to have the 'trusted-bridge' role. - * @param _to The recipient of the minted tokens. - * @param _amount The amount of tokens to mint. - */ - function crosschainMint(address _to, uint256 _amount) external { - // Assume access control is handled by the trusted bridge mechanism - _erc20BridgeableMod.crosschainMint(_to, _amount); + function exampleCrosschainBurn(address _token, address _to, uint256 _amount) external { + // Ensure the caller has the 'trusted-bridge' role before calling + ERC20_BRIDGEABLE.crosschainBurn(_token, _to, _amount); } - /** - * @notice Burns ERC20 tokens on a different chain. - * @dev Requires the caller to have the 'trusted-bridge' role. - * @param _from The sender of the tokens to burn. - * @param _amount The amount of tokens to burn. - */ - function crosschainBurn(address _from, uint256 _amount) external { - // Assume access control is handled by the trusted bridge mechanism - _erc20BridgeableMod.crosschainBurn(_from, _amount); + function exampleCrosschainMint(address _token, address _to, uint256 _amount) external { + // Ensure the caller has the 'trusted-bridge' role before calling + ERC20_BRIDGEABLE.crosschainMint(_token, _to, _amount); } }`} @@ -419,19 +400,19 @@ contract ERC20BridgeableFacet { ## Best Practices -- Only addresses with the `trusted-bridge` role can call `crosschainMint` and `crosschainBurn`. -- Ensure the `AccessControl` facet is properly configured with trusted bridge addresses before deploying this module. -- Handle `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, and `ERC20InvalidReciever` errors appropriately in your facet logic. +- Ensure the `trusted-bridge` role is correctly configured in the AccessControl facet before using cross-chain functions. +- Handle potential `ERC20InsufficientBalance`, `ERC20InvalidReciever`, or `ERC20InvalidSender` errors gracefully. +- Verify that the token address provided to `crosschainBurn` and `crosschainMint` is a valid ERC20 token contract. ## Integration Notes -This module interacts with the ERC20 storage slot defined by the diamond proxy. The `getERC20Storage` function provides direct access to this storage. The `checkTokenBridge` internal function enforces trust for cross-chain operations, relying on the `trusted-bridge` role within the `AccessControl` facet. +This module interacts with the diamond's storage using predefined slots for ERC20 and AccessControl data. The `getERC20Storage` and `getAccessControlStorage` functions provide access to these structs. The `checkTokenBridge` internal function relies on the `trusted-bridge` role managed by the AccessControl facet. Any changes to the diamond's storage layout that affect these slots may break this module's functionality.
- + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx index 72f662a2..fdb26a6c 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; ERC-20 Bridgeable extension for ERC-20 tokens. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx index 644fb0c4..61edbbcf 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20PermitFacet" -description: "Handles EIP-2612 permit functionality for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +description: "Manages ERC-20 token approvals with EIP-2612 permit functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Handles EIP-2612 permit functionality for ERC-20 tokens. +Manages ERC-20 token approvals with EIP-2612 permit functionality. -- Implements EIP-2612 permit functionality. -- Enables gas-less token approvals via signed messages. -- Provides `nonces` for replay protection. -- Exposes `DOMAIN_SEPARATOR` for signature verification. +- Implements EIP-2612 permit functionality for ERC-20 tokens. +- Provides `nonces`, `DOMAIN_SEPARATOR`, and `permit` functions for signature verification and allowance setting. +- Enhances gas efficiency by allowing off-chain signature generation for approvals. ## Overview -The ERC20PermitFacet enables gas-less approvals for ERC-20 tokens by allowing users to sign permit messages off-chain. This facet integrates EIP-2612 permit functionality, reducing transaction costs for users and improving the user experience for token transfers and approvals. +The ERC20PermitFacet enables EIP-2612 compliant token approvals, allowing users to grant allowances to spenders via signed messages. This enhances gas efficiency by enabling off-chain signing of approvals, reducing the need for direct on-chain transactions for this purpose. --- @@ -288,33 +287,20 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; -import {DiamondLoupeFacet} from "@compose-protocol/diamond-governance/facets/DiamondLoupe/DiamondLoupeFacet.sol"; - -contract MyDiamond is DiamondLoupeFacet { - // ... other facets - address public constant ERC20_PERMIT_FACET_ADDRESS = address(0x...); // Replace with actual facet address - - function permitERC20(address token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Delegate call to the ERC20PermitFacet - (bool success, ) = ERC20_PERMIT_FACET_ADDRESS.call(abi.encodeWithSelector( - ERC20PermitFacet.permit.selector, - token, - owner, - spender, - value, - deadline, - v, - r, - s - )); - require(success, "ERC20PermitFacet: permit call failed"); - } +import {DiamondLoupeFacet} from "@diamond-labs/diamond-runtime/facets/DiamondLoupeFacet.sol"; + +contract Consumer { + address constant DIAMOND_ADDRESS = 0x1234567890abcdef1234567890abcdef1234567890; + + // Assuming ERC20PermitFacet is added to the diamond + // and its selector is known. + function consumePermit(address tokenAddress, address spender, uint256 amount, uint256 deadline, bytes calldata signature) external { + // Get the ERC20PermitFacet interface + IERC20Permit permitFacet = IERC20Permit(DIAMOND_ADDRESS); - // Example of calling permit via a helper function - function approveToken(address token, address spender, uint256 amount) external { - // Obtain permit details off-chain and pass them here - // ... obtain v, r, s, deadline, owner, value - permitERC20(token, msg.sender, spender, amount, /* deadline */, /* v */, /* r */, /* s */); + // Call the permit function on the diamond + // Note: The actual diamond implementation will route this to the ERC20PermitFacet. + permitFacet.permit(tokenAddress, msg.sender, spender, amount, deadline, signature); } }`} @@ -322,19 +308,18 @@ contract MyDiamond is DiamondLoupeFacet { ## Best Practices -- Integrate this facet to enable gas-less token approvals for your ERC-20 tokens. -- Ensure the `DOMAIN_SEPARATOR` is correctly computed and used in signature generation to prevent cross-chain replay attacks. -- Store the `nonces` mapping securely within the diamond's storage. +- Integrate the ERC20PermitFacet into your diamond to leverage EIP-2612 permit functionality for gas-efficient approvals. +- Ensure the `permit` function is correctly called with a valid EIP-712 compliant signature, including the correct domain separator and nonce. ## Security Considerations -The `permit` function relies on off-chain signed messages. Ensure that the signature verification logic is robust and that the `owner`'s private key is securely managed. The `nonces` mapping must be updated atomically with the allowance change to prevent replay attacks. Reentrancy is not a direct concern for the `permit` function itself, but downstream token transfers should be audited. +The `permit` function is susceptible to signature replay attacks if the `DOMAIN_SEPARATOR` is not unique per chain and contract, or if nonces are not properly managed. Ensure that the nonce for each owner is incremented after each successful permit usage. The `ERC2612InvalidSignature` and `ERC20InvalidSpender` errors are emitted on invalid signatures or incorrect spender addresses, respectively.
- + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx index 5c7081a6..9d2c1ad7 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20PermitMod" -description: "Enables ERC-2612 permit functionality for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +description: "Implement ERC-2612 permit functionality for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Enables ERC-2612 permit functionality for ERC-20 tokens. +Implement ERC-2612 permit functionality for ERC-20 tokens. -- Implements ERC-2612 Permit standard for gasless approvals. -- Generates and validates EIP-712 domain separators for secure signing. -- Provides a dedicated storage slot for permit-related state. +- Implements EIP-2612 Permit functionality for ERC-20 tokens. +- Provides a reusable library for domain separator generation and signature verification. +- Isolates complex signature logic, enabling cleaner facet implementations. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the necessary logic and storage for implementing the ERC-2612 Permit standard. It allows users to grant token allowances via signed messages, enhancing user experience by reducing gas costs for frequent transfers. The module ensures secure domain separation for signature validation. +This module provides the core logic and storage for implementing the ERC-2612 Permit functionality. It allows users to grant token allowances via signed messages, enhancing gas efficiency and user experience. By isolating this logic, facets can easily integrate permit capabilities without duplicating complex signature verification and allowance setting code. --- @@ -236,42 +236,57 @@ address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, b {`pragma solidity ^0.8.30; -import {LibERC20Permit} from "@compose/modules/erc20/ERC20Permit.sol"; -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; +import {IERC20PermitMod, IERC20PermitStorage} from "@compose/modules/erc20/ERC20PermitMod.sol"; contract MyERC20Facet { - using LibERC20Permit for LibERC20Permit.ERC20PermitStorage; + // Assume ERC20PermitMod is deployed and its address is known + address immutable erc20PermitModAddress; - LibERC20Permit.ERC20PermitStorage internal _permitStorage; - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Retrieve the diamond storage for the ERC20Permit module - LibERC20Permit.ERC20PermitStorage storage permitStorage = LibERC20Permit.getPermitStorage(LibERC20Permit.getERC20Storage()); + constructor(address _erc20PermitModAddress) { + erc20PermitModAddress = _erc20PermitModAddress; + } - // Call the permit function from the library - permitStorage.permit(owner, spender, value, deadline, v, r, s); + /** + * @notice Allows a user to permit an operator by signing a permit message. + * @param _owner The owner of the tokens. + * @param _spender The address to grant allowance to. + * @param _value The amount of tokens to allow. + * @param _deadline The timestamp after which the permit is invalid. + * @param _v The v component of the signature. + * @param _r The r component of the signature. + * @param _s The s component of the signature. + */ + function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { + // Call the module's permit function. The module will handle signature validation + // and setting the allowance. The Approval event MUST be emitted by the calling facet. + (bool success, bytes memory data) = erc20PermitModAddress.call(abi.encodeWithSelector(IERC20PermitMod.permit.selector, _owner, _spender, _value, _deadline, _v, _r, _s)); + require(success, "ERC20PermitMod: permit call failed"); + + // Emit the Approval event as required by ERC-20 and ERC-2612 + emit Approval(_owner, _spender, _value); } - // Other ERC20 functions like allowance, approve, transferFrom... -}`} + // Other ERC-20 functions... +} +`} ## Best Practices -- Ensure the calling facet correctly implements the `Approval` event emission after a successful `permit` call. -- Validate the `deadline` parameter to prevent stale permit approvals. -- Use the `DOMAIN_SEPARATOR` provided by the module for accurate signature generation by off-chain signers. +- Ensure the `permit` function in your facet emits the `Approval` event after a successful call to the module, as required by the ERC-2612 standard. +- Validate the `_deadline` parameter to prevent the use of stale permits. +- Implement appropriate access control for the `permit` function if necessary, though it is typically permissionless. ## Integration Notes -The `LibERC20Permit` module manages its own dedicated storage, which is accessed via `LibERC20Permit.getPermitStorage(LibERC20Permit.getERC20Storage())`. Facets interacting with this module must ensure they have the correct storage layout and call the `permit` function, which will internally handle signature validation and allowance updates. The `Approval` event must be emitted by the calling facet after the library function successfully executes. +This module requires access to specific storage slots for ERC-20 token data and permit-specific data. The `getERC20Storage` and `getPermitStorage` functions are internal helpers to access these slots. Facets integrating this module must ensure that their storage layout is compatible or that they correctly delegate to this module's storage access. The `permit` function in the calling facet must emit the `Approval` event to adhere to ERC-20 and ERC-2612 standards.
- + diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx index 25d1aee6..9fc0a035 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/index.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; ERC-20 Permit extension for ERC-20 tokens. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/index.mdx b/website/docs/library/token/ERC20/index.mdx index f7559880..2e3a8827 100644 --- a/website/docs/library/token/ERC20/index.mdx +++ b/website/docs/library/token/ERC20/index.mdx @@ -11,4 +11,26 @@ import Icon from '@site/src/components/ui/Icon'; ERC-20 fungible token implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx index c0177e63..d3552b4b 100644 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC6909Facet" -description: "Manages ERC-6909 token balances and operator permissions." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +description: "Manages fungible and non-fungible tokens via ERC-6909." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC6909/ERC6909/ERC6909Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-6909 token balances and operator permissions. +Manages fungible and non-fungible tokens via ERC-6909. -- Implements ERC-6909 standard for token management. -- Supports both fungible and non-fungible token IDs. -- Enables granular approval and operator delegation mechanisms. -- Provides essential query functions like `balanceOf` and `allowance`. +- Implements the ERC-6909 standard for token management. +- Supports both fungible and non-fungible token types through a unified interface. +- Provides functions for direct transfers, allowance management, and operator delegation. ## Overview -The ERC6909Facet implements the ERC-6909 standard, enabling fungible and non-fungible token management within a Compose diamond. It handles balance tracking, approvals, and operator roles, providing a standardized interface for token operations. +The ERC6909Facet implements the ERC-6909 standard for managing fungible and non-fungible assets within a Compose diamond. It provides core functionalities for checking balances, allowances, and managing operator permissions, enabling flexible token operations. --- @@ -482,25 +481,22 @@ error ERC6909InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC6909Facet} from "@compose/diamond/facets/ERC6909/IERC6909Facet.sol"; -import {IDiamondCut} from "@compose/diamond/core/IDiamondCut.sol"; +import {IERC6909 } from "@compose-chain/contracts/src/interfaces/IERC6909.sol"; +import { ERC6909Facet } from "@compose-chain/contracts/src/facets/ERC6909Facet.sol"; -contract ERC6909User { - address constant DIAMOND_ADDRESS = address(0x1234567890abcdef); // Replace with your diamond address +contract Consumer { + address diamondAddress; - function getUserBalance(uint256 _id) external view returns (uint256) { - // Calldata for ERC6909Facet's balanceOf function - bytes4 selector = IERC6909Facet.balanceOf.selector; - (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _id)); - require(success, "balanceOf call failed"); - return abi.decode(data, (uint256)); + function transferToken(uint256 _id, uint256 _amount, address _to) external { + IERC6909(diamondAddress).transfer(_id, _amount, _to); } - function approveToken(uint256 _id, address _spender, uint256 _amount) external { - // Calldata for ERC6909Facet's approve function - bytes4 selector = IERC6909Facet.approve.selector; - (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _id, _spender, _amount)); - require(success, "approve call failed"); + function approveToken(uint256 _id, uint256 _amount, address _spender) external { + IERC6909(diamondAddress).approve(_id, _amount, _spender); + } + + function balanceOfToken(uint256 _id, address _owner) external view returns (uint256) { + return IERC6909(diamondAddress).balanceOf(_id, _owner); } }`} @@ -508,19 +504,19 @@ contract ERC6909User { ## Best Practices -- Initialize operator roles and approvals carefully, as they grant significant permissions. -- When upgrading, ensure the storage layout of the ERC6909 facet is compatible to prevent data loss or corruption. -- Use `setOperator` to manage operator relationships efficiently, rather than repeated `approve` calls for general access. +- Initialize the ERC6909Facet with necessary parameters during diamond deployment. +- Ensure appropriate access controls are implemented at the diamond level for sensitive operations like `setOperator`. +- Store token data in a manner that aligns with the ERC-6909 storage layout to ensure compatibility and upgradeability. ## Security Considerations -Ensure proper access control is implemented at the diamond level for functions that modify state (e.g., `transfer`, `transferFrom`, `approve`, `setOperator`). Input validation for token IDs, amounts, senders, and receivers is handled by the facet's error conditions. Be mindful of reentrancy if interacting with external contracts in your diamond's logic that calls this facet. +Implement strict access controls on the diamond proxy level to prevent unauthorized calls to `setOperator`. Ensure that all token transfers and approvals adhere to the standard ERC-6909 logic to prevent reentrancy attacks and maintain state integrity. Validate all input parameters to prevent unexpected behavior or state corruption.
- + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx index 3760b355..61f31fac 100644 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC6909Mod" -description: "Manages ERC-6909 token logic including minting, burning, and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +description: "Implements ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC6909/ERC6909/ERC6909Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-6909 token logic including minting, burning, and transfers. +Implements ERC-6909 minimal multi-token logic. -- Supports standard ERC-6909 token operations: mint, burn, transfer, approve, setOperator. -- Utilizes inline assembly for efficient storage access via `getStorage`. -- Defines custom errors for specific failure conditions, enhancing gas efficiency and clarity. +- Supports minting, burning, transferring, and approving tokens by ID. +- Includes operator functionality for delegated transfers. +- Provides necessary storage layout and internal functions for ERC-6909 compliance. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC6909Mod module provides the core internal logic and storage for implementing the ERC-6909 standard within a Compose diamond. It enables efficient management of multi-token fungible assets, supporting standard operations like approvals, transfers, minting, and burning. +The ERC6909Mod provides the core logic and storage for implementing the ERC-6909 standard within a Compose diamond. It enables standard token operations like minting, burning, transferring, and approvals for multiple token IDs, ensuring composability and adherence to the standard. --- @@ -477,21 +477,18 @@ error ERC6909InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC6909Mod} from "./IERC6909Mod.sol"; +import {IERC6909, IERC6909Mod} from "@compose/contracts/src/modules/erc6909/interfaces/IERC6909.sol"; +import {ERC6909} from "@compose/contracts/src/modules/erc6909/facets/ERC6909.sol"; -contract ERC6909Facet { - IERC6909Mod public immutable erc6909Mod; - - constructor(address _diamondAddress) { - erc6909Mod = IERC6909Mod(_diamondAddress); - } - - function mint(uint256 _id, uint256 _amount, address _to) external { - erc6909Mod.mint(_id, _amount, _to); +contract MyERC6909Facet is ERC6909 { + function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + // Assume ownership checks or other logic is handled externally + transfer(_from, _to, _id, _amount); } - function transfer(uint256 _id, uint256 _amount, address _from, address _to) external { - erc6909Mod.transfer(_id, _amount, _from, _to); + function approveToken(address _spender, uint256 _id, uint256 _amount) external { + // Assume ownership checks or other logic is handled externally + approve(_spender, _id, _amount); } }`} @@ -499,19 +496,19 @@ contract ERC6909Facet { ## Best Practices -- Ensure proper access control is implemented in facets calling module functions. -- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` gracefully. -- Be mindful of potential reentrancy if facets implementing ERC6909 logic interact with external contracts. +- Ensure proper access control is implemented in calling facets for sensitive operations like minting and burning. +- Be mindful of allowance management; `type(uint256).max` bypasses deduction, and operators can transfer without deduction. +- Handle potential `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` errors gracefully in calling facets. ## Integration Notes -This module relies on a specific storage slot defined by `STORAGE_POSITION` for its internal state. Facets interacting with this module must ensure that this storage slot is correctly allocated and managed within the diamond's storage layout. The `getStorage` function provides a direct pointer to the `ERC6909Storage` struct, allowing facets to read and write token balances and allowances. +The ERC6909Mod defines a specific storage struct (`ERC6909Storage`) which must be laid out according to the Compose storage pattern. Facets interacting with this module should use the `getStorage` function (or access the storage slot directly if integrated via an initializer) to retrieve a pointer to this struct. Operations modify this shared storage, making them visible to all facets that implement ERC-6909 logic.
- + diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx index f146bbc5..3b2e2209 100644 --- a/website/docs/library/token/ERC6909/ERC6909/index.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; ERC-6909 minimal multi-token implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC6909/index.mdx b/website/docs/library/token/ERC6909/index.mdx index f146bbc5..b91f1e51 100644 --- a/website/docs/library/token/ERC6909/index.mdx +++ b/website/docs/library/token/ERC6909/index.mdx @@ -11,4 +11,12 @@ import Icon from '@site/src/components/ui/Icon'; ERC-6909 minimal multi-token implementations. -_No items in this category yet._ + + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx index d67a74e2..514cf687 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721BurnFacet" -description: "Burn ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +description: "Burn ERC-721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721/ERC721BurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC721 tokens within a Compose diamond. +Burn ERC-721 tokens within a Compose diamond. -- Supports burning of ERC721 tokens by ID. -- Emits standard `Transfer` events with `to` address set to the zero address upon burning. -- Integrates seamlessly with the Compose diamond storage pattern. +- Enables destruction of ERC-721 tokens. +- Integrates with diamond storage for efficient state management. +- Emits standard ERC-721 `Transfer` event upon successful burn. ## Overview -The ERC721BurnFacet provides the functionality to destroy ERC721 tokens directly within a Compose diamond. It integrates with the diamond's storage pattern to manage token states and emits standard ERC721 events upon successful burning. +The ERC721BurnFacet provides the functionality to destroy ERC-721 tokens. It integrates with the diamond's storage pattern to efficiently manage token state during the burn process, ensuring compliance with ERC-721 standards. --- @@ -171,21 +171,20 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId); {`pragma solidity ^0.8.30; import {IERC721BurnFacet} from "../facets/ERC721BurnFacet.sol"; -import {IDiamondProxy} from "@compose-protocol/diamond-proxy/contracts/IDiamondProxy.sol"; +import {IDiamondCut} from "../diamond/IDiamondCut.sol"; -contract ERC721BurnConsumer { - IDiamondProxy public diamondProxy; +contract Deployer { + address public diamondAddress; - constructor(address _diamondProxy) { - diamondProxy = IDiamondProxy(_diamondProxy); + function deploy() public { + // ... diamond deployment logic ... + diamondAddress = address(0xYourDiamondAddress); } - function burnToken(uint256 _tokenId) external { - bytes4 selector = IERC721BurnFacet.burn.selector; - // Note: The \`burn\` function requires approval or ownership. - // This example assumes the caller has the necessary permissions. - (bool success, ) = address(diamondProxy).call(abi.encodeWithSelector(selector, _tokenId)); - require(success, "ERC721BurnFacet: burn failed"); + function burnToken(address _toBurnToken, uint256 _tokenId) public { + IERC721BurnFacet burnFacet = IERC721BurnFacet(diamondAddress); + // Ensure sufficient approval or ownership before burning + burnFacet.burn(_toBurnToken, _tokenId); } }`} @@ -193,18 +192,19 @@ contract ERC721BurnConsumer { ## Best Practices -- Ensure the caller has ownership of the token or is approved to burn it before invoking the `burn` function via the diamond proxy. -- Properly initialize the diamond with this facet to enable token burning capabilities. +- Ensure the `ERC721BurnFacet` is correctly cut into the diamond, pointing to the appropriate contract address. +- Before calling `burn`, verify that the caller has the necessary permissions (owner of the token or approved by the owner). +- Handle `ERC721NonexistentToken` and `ERC721InsufficientApproval` errors appropriately. ## Security Considerations -The `burn` function requires the caller to have the necessary permissions (token ownership or operator approval). Insufficient approval will lead to a revert with `ERC721InsufficientApproval`. Access control is managed by the underlying ERC721 implementation within the diamond. +The `burn` function should only be callable by the token owner or an address approved by the token owner. The facet relies on the underlying ERC-721 implementation for ownership and approval checks. Access control for calling the `burn` function on the diamond proxy itself is managed by the diamond's access control mechanism.
- + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx index 4d3f8eab..36bfa34d 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721Facet" -description: "Manages ERC721 token standard operations within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721/ERC721Facet.sol" +description: "Manages ERC-721 token ownership, approvals, and metadata within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721/ERC721Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC721 token standard operations within a diamond. +Manages ERC-721 token ownership, approvals, and metadata within a diamond. -- Implements ERC721 standard for NFTs. -- Supports token transfers, approvals, and ownership queries. -- Provides access to token metadata via `tokenURI`. -- Includes internal transfer logic for composability. +- Full ERC-721 compliance, including safe transfer hooks. +- Direct access to token metadata via `tokenURI`. +- Comprehensive ownership and approval management functions. +- Internal `internalTransferFrom` for composable internal logic. ## Overview -The ERC721Facet provides the core functionality for managing non-fungible tokens (NFTs) on a Compose diamond. It implements the ERC721 standard, enabling token ownership tracking, transfers, approvals, and metadata retrieval, making the diamond capable of acting as an NFT collection contract. +The ERC721Facet provides a complete implementation of the ERC-721 Non-Fungible Token Standard. It enables token creation, transfers, ownership tracking, and approval management, surfacing these critical functionalities through the diamond proxy's unified interface. --- @@ -617,26 +617,31 @@ error ERC721InvalidOperator(address _operator); {`pragma solidity ^0.8.30; -import {IERC721Facet} from "@compose/diamond/facets/ERC721/IERC721Facet.sol"; -import {IDiamondRegistry} from "@compose/diamond/IDiamondRegistry.sol"; +import {IERC721Facet} from "@compose/contracts/src/facets/ERC721Facet.sol"; -contract MyERC721Diamond is IDiamondRegistry { - address constant ERC721_FACET_ADDRESS = address(0xYourERC721FacetAddress); +contract ERC721Consumer { + address immutable DIAMOND_FACET_CUTTER; + IERC721Facet immutable erc721Facet; - function name() external view returns (string memory) { - return IERC721Facet(ERC721_FACET_ADDRESS).name(); + constructor(address _diamondFacetCutter) { + DIAMOND_FACET_CUTTER = _diamondFacetCutter; + erc721Facet = IERC721Facet(DIAMOND_FACET_CUTTER); } - function symbol() external view returns (string memory) { - return IERC721Facet(ERC721_FACET_ADDRESS).symbol(); + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); } - function ownerOf(uint256 tokenId) external view returns (address) { - return IERC721Facet(ERC721_FACET_ADDRESS).ownerOf(tokenId); + function getTokenSymbol() external view returns (string memory) { + return erc721Facet.symbol(); } - function transferFrom(address from, address to, uint256 tokenId) external { - IERC721Facet(ERC721_FACET_ADDRESS).transferFrom(from, to, tokenId); + function getTokenOwner(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); + } + + function safeTransferToken(address from, address to, uint256 tokenId) external { + erc721Facet.safeTransferFrom(from, to, tokenId); } }`} @@ -644,19 +649,19 @@ contract MyERC721Diamond is IDiamondRegistry { ## Best Practices -- Deploy the ERC721Facet and register its selectors with the diamond proxy. -- Ensure the diamond has adequate access control for functions like `approve` and `transferFrom` if necessary. -- Handle token URIs carefully to ensure metadata consistency and immutability where intended. +- Ensure the ERC721Facet is correctly initialized with its storage slot and any required parameters during diamond deployment. +- Utilize the `ownerOf`, `balanceOf`, and `getApproved` functions to query token state before performing state-changing operations. +- Implement robust access control mechanisms for functions that mint or burn tokens if these capabilities are exposed through other facets. ## Security Considerations -The `internalTransferFrom` function includes critical checks for ownership and approvals. Ensure that access to functions like `approve` and `setApprovalForAll` is appropriately managed to prevent unauthorized token control. The `safeTransferFrom` functions include checks for receiver contract compatibility, mitigating reentrancy risks from malicious receiver contracts. +Input validation is crucial for all token-related operations, especially `tokenId`. Ensure that token IDs exist before attempting transfers or approvals to prevent `ERC721NonexistentToken` errors. The `safeTransferFrom` functions include checks for receiver compatibility, mitigating reentrancy risks related to token handling. Access to sensitive functions like minting or burning (if applicable through other facets) must be strictly controlled.
- + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx index 0f303f35..7a4a0d53 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721Mod" -description: "Manage ERC-721 tokens within a diamond proxy." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721/ERC721Mod.sol" +description: "Manage ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721/ERC721Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage ERC-721 tokens within a diamond proxy. +Manage ERC721 tokens within a Compose diamond. -- Permissionless minting and burning of ERC-721 tokens. -- Handles token ownership and approval logic internally. -- Utilizes diamond storage to maintain a consistent token state across facets. +- Implements core ERC-721 operations: mint, burn, and transfer. +- Utilizes inline assembly for efficient access to diamond storage. +- Enforces standard ERC-721 ownership and approval checks. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC721Mod provides core logic for managing ERC-721 compliant tokens. It abstracts away the complexities of token state management, allowing facets to implement ERC-721 functionality by interacting with shared diamond storage. +The ERC721Mod provides essential internal logic for ERC-721 token management, enabling custom facets to integrate robust token functionalities. It abstracts the complexities of ERC-721 state handling, ensuring safe and consistent token operations within the diamond's storage. --- @@ -314,45 +314,45 @@ error ERC721NonexistentToken(uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721Mod } from "@compose/core/src/modules/ERC721Mod.sol"; +import {IERC721Mod } from "@compose/modules/erc721/IERC721Mod.sol"; contract MyERC721Facet { IERC721Mod internal erc721Mod; - function initialize(address _erc721ModAddress) external { - erc721Mod = IERC721Mod(_erc721ModAddress); + constructor(address _diamondProxy) { + erc721Mod = IERC721Mod(_diamondProxy); } function mintToken(address _to, uint256 _tokenId) external { erc721Mod.mint(_to, _tokenId); } - function transferToken(address _from, address _to, uint256 _tokenId) external { - erc721Mod.transferFrom(_from, _to, _tokenId); - } - function burnToken(uint256 _tokenId) external { erc721Mod.burn(_tokenId); } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + erc721Mod.transferFrom(_from, _to, _tokenId); + } }`} ## Best Practices -- Ensure the `ERC721Mod` is initialized correctly via its facet's `initialize` function before calling any of its methods. -- Handle potential `ERC721` custom errors to provide informative feedback to users during token operations. -- Be mindful of gas costs associated with token transfers and burns, especially in loops. +- Ensure the `ERC721Mod` facet is correctly initialized with the diamond proxy address. +- Always validate token existence and ownership before calling `transferFrom` or `burn` if external checks are needed beyond the module's internal reverts. +- Handle custom errors like `ERC721IncorrectOwner` and `ERC721NonexistentToken` in facet logic for clear user feedback. ## Integration Notes -ERC721Mod interacts with diamond storage via a predefined storage slot. Facets using this module should ensure they do not conflict with this slot. The `getStorage` function can be used to inspect the raw ERC-721 storage, though direct manipulation is discouraged in favor of module functions. The module maintains invariants related to token existence, ownership, and approvals. +The ERC721Mod interacts with diamond storage using a predefined slot for its `ERC721Storage` struct. Facets using this module will access and modify token ownership, approvals, and metadata through the module's functions. Changes made by the module are immediately visible to all facets via the diamond proxy. The storage layout is defined within the module and should not be altered by other facets to maintain compatibility.
- + diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx index db4f299f..7ead6277 100644 --- a/website/docs/library/token/ERC721/ERC721/index.mdx +++ b/website/docs/library/token/ERC721/ERC721/index.mdx @@ -11,4 +11,26 @@ import Icon from '@site/src/components/ui/Icon'; ERC-721 non-fungible token implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx index 0622e03a..b79bd297 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableBurnFacet" -description: "Burn ERC721 tokens and manage enumeration state" -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +description: "Burn ERC721 tokens and manage enumeration." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC721 tokens and manage enumeration state +Burn ERC721 tokens and manage enumeration. -- Enables the burning of ERC721 tokens. -- Automatically updates internal enumeration tracking upon token burn. -- Emits a `Transfer` event for burned tokens, signifying their removal from circulation. +- Allows burning of ERC721 tokens. +- Integrates with enumeration tracking to remove burned tokens. +- Emits `Transfer` event upon successful token burn. ## Overview -This facet provides functionality to burn ERC721 tokens. It integrates with the enumeration tracking mechanisms to ensure that burned tokens are correctly removed from the tracked list, maintaining the integrity of the token supply. +The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens within a Compose diamond. It ensures that burned tokens are correctly removed from enumeration tracking, maintaining the integrity of the token supply and ownership lists. --- @@ -185,14 +185,19 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721EnumerableBurn } from "@compose-protocol/core/contracts/facets/ERC721/IERC721EnumerableBurn.sol"; +import {IERC721EnumerableBurnFacet} from "../facets/IERC721EnumerableBurnFacet.sol"; -contract ExampleConsumer { - IERC721EnumerableBurn private constant ERC721_ENUMERABLE_BURN_FACET = IERC721EnumerableBurn(0x...); // Diamond proxy address +contract MyDiamondConsumer { + IERC721EnumerableBurnFacet immutable erc721BurnFacet; - function burnToken(uint256 tokenId) external { - // Assume appropriate approvals and ownership checks are handled by access control or other facets - ERC721_ENUMERABLE_BURN_FACET.burn(tokenId); + constructor(address _diamondAddress) { + // Assume _diamondAddress is the address of your deployed diamond proxy + erc721BurnFacet = IERC721EnumerableBurnFacet(_diamondAddress); + } + + function burnToken(uint256 _tokenId) public { + // Directly call the burn function on the diamond proxy + erc721BurnFacet.burn(_tokenId); } }`} @@ -200,18 +205,19 @@ contract ExampleConsumer { ## Best Practices -- Ensure proper access control is implemented before calling the `burn` function to prevent unauthorized token destruction. -- Integrate with other ERC721 facets (like `ERC721Receiver`) to handle post-burn logic if necessary. +- Ensure the `ERC721EnumerableBurnFacet` is correctly registered with the diamond proxy before use. +- Always verify token ownership and necessary approvals before calling the `burn` function to prevent `ERC721InsufficientApproval` errors. +- Consider the implications of burning on token enumeration order and total supply. ## Security Considerations -The `burn` function requires careful access control to prevent unauthorized burning of tokens. Ensure that only the token owner or an authorized operator can call this function. The `ERC721NonexistentToken` error is raised if the token ID does not exist, and `ERC721InsufficientApproval` is raised if the caller does not have sufficient approval to burn the token. +This facet's `burn` function requires careful access control to be implemented at the diamond level or within calling contracts to ensure only authorized parties can burn tokens. The function itself does not perform ownership checks; these must be handled by the caller or enforced by other facets. Incorrect usage could lead to unintended token destruction. The `ERC721NonexistentToken` error is emitted if the token ID does not exist.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx index df881122..72b92335 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableFacet" -description: "Enumerable ERC-721 functionality for token management." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +description: "Enumerable ERC-721 token management and querying." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Enumerable ERC-721 functionality for token management. +Enumerable ERC-721 token management and querying. -- Tracks total token supply and individual owner balances. -- Allows retrieval of token IDs by owner and index, enabling enumeration. -- Supports standard ERC-721 metadata and approval mechanisms. +- Full ERC-721 functionality with enumeration. +- `tokenOfOwnerByIndex` enables querying tokens by owner and index. +- Supports `safeTransferFrom` for secure token transfers to contract recipients. ## Overview -This facet provides standard ERC-721 enumerable features, allowing for the tracking of total supply, token ownership counts, and specific token IDs by owner and index. It integrates seamlessly into a Compose diamond, extending its capabilities with a comprehensive NFT implementation. +This facet provides comprehensive ERC-721 compliant functionality, including token enumeration capabilities. It allows querying token names, symbols, URIs, total supply, individual token ownership, balances, and approvals. The `tokenOfOwnerByIndex` function is key for iterating through a user's tokens. --- @@ -690,28 +690,30 @@ error ERC721OutOfBoundsIndex(address _owner, uint256 _index); {`pragma solidity ^0.8.30; -import {IERC721EnumerableFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; +import {IERC721EnumerableFacet} from "@compose-protocol/diamond/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; contract ERC721EnumerableConsumer { - IERC721EnumerableFacet public immutable erc721Facet; + IERC721EnumerableFacet immutable erc721Facet; constructor(address _diamondAddress) { erc721Facet = IERC721EnumerableFacet(_diamondAddress); } - function getTotalSupply() external view returns (uint256) { + function getTokenSupply() external view returns (uint256) { return erc721Facet.totalSupply(); } - function getOwnerBalance(address _owner) external view returns (uint256) { - return erc721Facet.balanceOf(_owner); + function getTokenOwner(uint256 _tokenId) external view returns (address) { + return erc721Facet.ownerOf(_tokenId); } - function getTokenByIndex(uint256 _index) external view returns (uint256) { - // Assuming a valid owner address is known or derived - // For demonstration, let's assume a known owner - address owner = address(0x123); // Replace with actual owner derivation - return erc721Facet.tokenOfOwnerByIndex(owner, _index); + function getTokensOwned(address _owner) external view returns (uint256[] memory) { + uint256 count = erc721Facet.balanceOf(_owner); + uint256[] memory tokenIds = new uint256[](count); + for (uint256 i = 0; i < count; i++) { + tokenIds[i] = erc721Facet.tokenOfOwnerByIndex(_owner, i); + } + return tokenIds; } }`} @@ -719,19 +721,19 @@ contract ERC721EnumerableConsumer { ## Best Practices -- Ensure proper initialization of the ERC721 storage within the diamond's deployment process. -- Utilize the `internalTransferFrom` function internally for state transitions to maintain consistency. -- Grant `approve` and `setApprovalForAll` permissions judiciously to manage token access. +- Use `tokenOfOwnerByIndex` carefully for large token balances, as it requires multiple calls and can be gas-intensive. +- Ensure proper access control is implemented at the diamond level for functions like `approve` and `transferFrom`. +- When upgrading the diamond, ensure the storage layout of this facet remains compatible. ## Security Considerations -Access control for `approve` and `setApprovalForAll` is crucial to prevent unauthorized token management. Ensure that token transfers (`transferFrom`, `safeTransferFrom`) are routed through the diamond proxy to maintain state integrity and enforce access controls. Be mindful of potential reentrancy if external contracts interact directly with token URIs or metadata. +The `internalTransferFrom` function is intended for internal use by the facet itself and should not be directly exposed. Ensure that external calls to `transferFrom` and `safeTransferFrom` are properly validated for sender and receiver permissions. `approve` and `setApprovalForAll` require caller authorization. Reentrancy is mitigated by standard ERC721 patterns and the diamond proxy architecture.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx index 6ab9c144..054a744d 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableMod" -description: "Manages enumerable ERC-721 token state within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +description: "Manages enumerable ERC-721 tokens within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages enumerable ERC-721 token state within a diamond. +Manages enumerable ERC-721 tokens within a diamond. -- Manages token IDs for enumeration (e.g., `tokenByIndex`). -- Handles state updates for `mint`, `burn`, and `transferFrom` operations. -- Provides access to the internal ERC-721 enumerable storage struct. +- Manages token IDs for enumeration (e.g., `tokenOfOwnerByIndex`, `tokenByIndex`). +- Integrates seamlessly with diamond storage via predefined slots. +- Provides core ERC-721 transfer, mint, and burn logic with enumeration updates. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC721EnumerableMod provides the core logic for tracking and managing ERC-721 token ownership and enumeration. It ensures that minted, burned, and transferred tokens are correctly reflected in the token count and ownership lists, enabling compliant ERC-721 behavior within a diamond proxy. +The ERC721EnumerableMod provides the core logic for managing enumerable ERC-721 tokens. It ensures that tokens are correctly added to and removed from internal tracking structures upon minting, burning, and transferring. This module enables facets to implement full ERC-721 compliance with token enumeration capabilities. --- @@ -297,26 +297,28 @@ error ERC721NonexistentToken(uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721EnumerableMod} from "@compose/modules/ERC721EnumerableMod.sol"; -import {ERC721Storage} from "@compose/storage/ERC721Storage.sol"; +import {IERC721EnumerableMod, IERC721EnumerableStorage} from "@compose/modules/erc721/ERC721EnumerableMod.sol"; contract MyERC721Facet { - IERC721EnumerableMod public immutable erc721Mod; + IERC721EnumerableMod internal immutable erc721EnumerableMod; constructor(address _diamondProxy) { - erc721Mod = IERC721EnumerableMod(_diamondProxy); + // Assume diamond proxy address is known + erc721EnumerableMod = IERC721EnumerableMod(_diamondProxy); } function mintToken(address _to, uint256 _tokenId) external { - erc721Mod.mint(_to, _tokenId); + // Access storage via getStorage before minting to check existence if needed + // This example directly calls mint for simplicity + erc721EnumerableMod.mint(_to, _tokenId); } - function burnToken(uint256 _tokenId) external { - erc721Mod.burn(_tokenId); + function transferToken(address _from, address _to, uint256 _tokenId) external { + erc721EnumerableMod.transferFrom(_from, _to, _tokenId); } - function transferToken(address _from, address _to, uint256 _tokenId) external { - erc721Mod.transferFrom(_from, _to, _tokenId); + function burnToken(uint256 _tokenId) external { + erc721EnumerableMod.burn(_tokenId); } }`} @@ -324,19 +326,19 @@ contract MyERC721Facet { ## Best Practices -- Ensure correct ownership and approval checks are performed before calling `transferFrom` or `burn`. -- Handle potential reverts from `mint`, `burn`, and `transferFrom` appropriately in facet logic. -- Understand that `ERC721EnumerableMod` modifies shared ERC-721 storage; coordinate with other ERC-721 facets. +- Ensure proper access control is implemented in facets calling this module's functions (e.g., minting, burning). +- Always validate token existence and ownership before attempting transfers or burns via facet logic. +- Be mindful of storage slot collisions if this module's storage layout is modified or extended. ## Integration Notes -This module interacts with the `ERC721Storage` struct, specifically managing arrays for token enumeration. Facets using this module should be aware that operations like minting and burning modify shared state visible to all ERC-721 facets. The module relies on the standard ERC-721 storage layout and slot definitions. +This module relies on a specific storage slot for its `ERC721EnumerableStorage` struct. Facets interacting with this module should use the `getStorage` function to access the storage directly or call module functions which implicitly use it. The module's internal state is updated atomically during mint, burn, and transfer operations. No specific ordering is required for facet addition, but its storage slot must remain consistent.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx index 8ae400ee..7eb0ae8a 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx @@ -11,4 +11,26 @@ import Icon from '@site/src/components/ui/Icon'; ERC-721 Enumerable extension for ERC-721 tokens. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC721/index.mdx b/website/docs/library/token/ERC721/index.mdx index db4f299f..e3dc8b77 100644 --- a/website/docs/library/token/ERC721/index.mdx +++ b/website/docs/library/token/ERC721/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; ERC-721 non-fungible token implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx index ecc7520d..d3b3c340 100644 --- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "RoyaltyFacet" description: "Manages token royalties according to ERC-2981." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/Royalty/RoyaltyFacet.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/Royalty/RoyaltyFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -26,13 +26,14 @@ Manages token royalties according to ERC-2981. - Implements ERC-2981 `royaltyInfo` function. -- Supports token-specific and default royalty configurations. -- Calculates royalty amounts based on sale price using basis points. +- Supports both default and token-specific royalty configurations. +- Calculates royalty fees as a percentage (basis points) of the sale price. +- Utilizes inline assembly for efficient storage access. ## Overview -The RoyaltyFacet implements the ERC-2981 standard, enabling royalty payments for NFTs sold on Compose diamonds. It provides functions to retrieve royalty information for specific tokens or a default royalty configuration, ensuring creators are compensated on secondary market sales. +The RoyaltyFacet implements the ERC-2981 standard for royalty payments. It provides functions to retrieve royalty information for a given token and sale price, supporting both token-specific and default royalty configurations. This facet enables marketplaces and other dApps to correctly distribute royalties. --- @@ -151,22 +152,35 @@ Returns royalty information for a given token and sale price. Returns token-spec {`pragma solidity ^0.8.30; -import {IRoyaltyFacet} from "@compose-protocol/diamond/contracts/facets/Royalty/IRoyaltyFacet.sol"; +import {IDiamondCut, IDiamondLoupe} from "@compose/diamond-contracts/contracts/interfaces/IDiamond.sol"; +import {IRoyaltyFacet} from "@compose/diamond-contracts/contracts/facets/Royalty/IRoyaltyFacet.sol"; -contract RoyaltyConsumer { - address immutable DIAMOND_ADDRESS; - bytes4 private constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; +contract DeployDiamond { + // ... deployment setup ... - constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; + function deploy() external { + // ... other facet deployments ... + + address royaltyFacet = address(new RoyaltyFacet()); + facetCuts.push(IDiamondCut.FacetCut({ + facetAddress: royaltyFacet, + action: IDiamondCut.Action.Add, + functionSelectors: + bytes4.concat(IRoyaltyFacet.royaltyInfo.selector) + })); + + // ... diamond cut execution ... } +} + +contract MyMarketplace { + // Assuming the diamond proxy is deployed at address \`diamondProxy\` + IRoyaltyFacet public royaltyFacet = IRoyaltyFacet(diamondProxy); - function getRoyaltyDetails(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 royaltyAmount) { - bytes memory data = abi.encodeWithSelector(ROYALTY_INFO_SELECTOR, tokenId, salePrice); - (bool success, bytes memory returnData) = DIAMOND_ADDRESS.staticcall(data); - require(success, "Royalty query failed"); - (receiver, royaltyAmount) = abi.decode(returnData, (address, uint256)); - return (receiver, royaltyAmount); + function getSaleRoyalty(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 feeBasisPoints) { + // Calls the royaltyInfo function through the diamond proxy + (receiver, feeBasisPoints) = royaltyFacet.royaltyInfo(tokenId, salePrice); + return (receiver, feeBasisPoints); } }`} @@ -174,18 +188,19 @@ contract RoyaltyConsumer { ## Best Practices -- Integrate the RoyaltyFacet into your diamond to enable ERC-2981 compliant royalty distributions. -- Configure token-specific royalties or a default royalty rate during diamond initialization. +- Initialize royalty settings (default and token-specific) via a separate facet or deployment script after deploying the RoyaltyFacet. +- Ensure the `royaltyInfo` function is called through the diamond proxy to correctly dispatch the call to the facet. +- Store royalty data efficiently, considering the diamond storage pattern to avoid slot collisions. ## Security Considerations -The `royaltyInfo` function is read-only and uses `staticcall`, mitigating reentrancy risks. Ensure the royalty basis points and receiver addresses are correctly configured during deployment and upgrades to prevent unintended royalty distributions. +Access control for setting default and token-specific royalties should be managed by a separate facet (e.g., an ownership or admin facet). The `royaltyInfo` function itself is read-only and does not present reentrancy risks. Ensure the storage slot for royalty data is unique to prevent conflicts with other facets.
- + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx index d2af8687..c041dec5 100644 --- a/website/docs/library/token/Royalty/RoyaltyMod.mdx +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "RoyaltyMod" -description: "Manages ERC-2981 royalties with token-specific and default settings." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/token/Royalty/RoyaltyMod.sol" +description: "Manages ERC-2981 royalties for tokens and defaults." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/Royalty/RoyaltyMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,12 +21,12 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-2981 royalties with token-specific and default settings. +Manages ERC-2981 royalties for tokens and defaults. - Supports both default and token-specific royalty configurations. -- Implements ERC-2981 `royaltyInfo` function logic, including fallback to default settings. +- Implements ERC-2981 `royaltyInfo` function logic, including fallback to defaults. - Provides functions to set, delete, and reset royalty information. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides a robust implementation for ERC-2981 royalty standards, enabling both default royalty settings and token-specific overrides. It ensures that royalty information is always retrievable, either from token-specific configurations or a fallback default, enhancing composability for NFT marketplaces and secondary sales. +This module provides robust ERC-2981 royalty enforcement by managing both default and token-specific royalty configurations. It allows setting, retrieving, and deleting royalty information, ensuring compliance with the standard and enabling revenue sharing for NFTs. --- @@ -299,36 +299,26 @@ error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); {`pragma solidity ^0.8.30; -import {IRoyaltyMod} from "./interfaces/IRoyaltyMod.sol"; +import {IRoyaltyMod} from "@compose/diamond-contracts/contracts/modules/royalty/IRoyaltyMod.sol"; +import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol"; -contract RoyaltyConsumerFacet { - address immutable DIAMOND_ADDRESS; - IRoyaltyMod immutable royaltyMod; +contract MyFacet is IRoyaltyMod { + address immutable _diamondAddress; constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; - royaltyMod = IRoyaltyMod(diamondAddress); + _diamondAddress = diamondAddress; } - /** - * @notice Sets a royalty for a specific token. - * @param _tokenId The ID of the token. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee in basis points. - */ - function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); + function _getRoyaltyMod() internal view returns (IRoyaltyMod) { + return IRoyaltyMod(_diamondAddress); } - /** - * @notice Gets royalty information for a token. - * @param _tokenId The ID of the token. - * @param _salePrice The sale price of the token. - * @return receiver The address receiving royalties. - * @return feeAmount The amount of royalties to be paid. - */ - function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 feeAmount) { - return royaltyMod.royaltyInfo(_tokenId, _salePrice); + function exampleSetRoyalty(uint256 tokenId, address receiver, uint16 basisPoints) external { + _getRoyaltyMod().setTokenRoyalty(tokenId, receiver, basisPoints); + } + + function exampleRoyaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint16 basisPoints) { + return _getRoyaltyMod().royaltyInfo(tokenId, salePrice); } }`} @@ -336,19 +326,19 @@ contract RoyaltyConsumerFacet { ## Best Practices -- Ensure receiver addresses are validated before setting royalties to prevent unintended distributions. -- Utilize `deleteDefaultRoyalty` and `resetTokenRoyalty` judiciously to manage royalty configurations effectively. -- Be aware that royalty information is accessed via the diamond proxy, ensuring consistent behavior across facet upgrades. +- Use `setDefaultRoyalty` for global royalty settings and `setTokenRoyalty` for specific exceptions. +- Validate receiver addresses and basis points before setting royalties to prevent errors. +- Be aware that `resetTokenRoyalty` will revert token-specific settings to the default. ## Integration Notes -The RoyaltyMod stores its state in a dedicated slot within the diamond's storage. The `getStorage` function provides direct access to this storage struct. Facets can interact with royalty logic by calling the external functions exposed by this module through the diamond proxy. Changes to default or token-specific royalties are immediately reflected in subsequent calls to `royaltyInfo`. +The RoyaltyMod utilizes a predefined storage slot to store its royalty configuration. The `getStorage` function provides direct access to this storage struct. Facets interacting with royalty logic should call the functions defined in `IRoyaltyMod` to ensure proper interaction with the diamond's storage and maintain consistency.
- + diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx index 8ae51d9d..76b855c8 100644 --- a/website/docs/library/token/Royalty/index.mdx +++ b/website/docs/library/token/Royalty/index.mdx @@ -11,4 +11,19 @@ import Icon from '@site/src/components/ui/Icon'; ERC-2981 royalty standard implementations. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/index.mdx b/website/docs/library/token/index.mdx index 50f27698..e18f1fe8 100644 --- a/website/docs/library/token/index.mdx +++ b/website/docs/library/token/index.mdx @@ -11,4 +11,40 @@ import Icon from '@site/src/components/ui/Icon'; Token standard implementations for Compose diamonds. -_No items in this category yet._ + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx index 047c1f79..6be28afc 100644 --- a/website/docs/library/utils/NonReentrancyMod.mdx +++ b/website/docs/library/utils/NonReentrancyMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "NonReentrancyMod" -description: "Prevent reentrant calls within diamond functions." -gitSource: "https://github.com/maxnorm/Compose/blob/fc8e8677d3c3fe32a459f173c58ec546dfcb9e13/src/libraries/NonReentrancyMod.sol" +description: "Prevent reentrant calls within facets." +gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/libraries/NonReentrancyMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Prevent reentrant calls within diamond functions. +Prevent reentrant calls within facets. -- Provides `enter()` and `exit()` functions to manage reentrancy state. -- Uses a simple state variable to track reentrancy status, minimizing storage overhead. -- Designed for easy integration into any facet. +- Provides `enter()` and `exit()` functions to manage reentrancy locks. +- Utilizes a dedicated storage slot for reentrancy state, ensuring isolation. +- Guards against reentrancy attacks by preventing recursive calls within protected functions. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The NonReentrancyMod provides essential guards to prevent reentrant function calls, a common vulnerability in smart contracts. By integrating these checks, facets can ensure that sensitive operations are not executed multiple times before the initial execution completes, maintaining data integrity and preventing unexpected state changes within the diamond. +The NonReentrancyMod provides essential functionality to prevent reentrant function calls within your diamond facets. By implementing checks before and after critical operations, it ensures the integrity of state transitions and guards against common reentrancy attacks. --- @@ -96,24 +96,21 @@ error Reentrancy(); {`pragma solidity ^0.8.30; -import {LibNonReentrancy} from "@compose/contracts/src/module/lib/LibNonReentrancy.sol"; +import {LibNonReentrancy} from "./libraries/LibNonReentrancy.sol"; contract MyFacet { - using LibNonReentrancy for uint256; + using LibNonReentrancy for LibNonReentrancy.NonReentrancyStorage; - uint256 internal _nonReentrancyState; + LibNonReentrancy.NonReentrancyStorage private _nonReentrancyStorage; - /** - * @notice Example function protected by non-reentrancy guard. - */ - function protectedAction() external { - // Lock reentrancy before executing sensitive logic. - _nonReentrancyState.enter(); + function doSomethingImportant() external { + // Acquire reentrancy lock + _nonReentrancyStorage.enter(); - // ... sensitive logic here ... + // ... perform state-changing operations ... - // Unlock reentrancy after sensitive logic is complete. - _nonReentrancyState.exit(); + // Release reentrancy lock + _nonReentrancyStorage.exit(); } }`} @@ -121,19 +118,19 @@ contract MyFacet { ## Best Practices -- Always call `enter()` at the beginning of a function before any external calls or state-changing operations that could lead to reentrancy. -- Always call `exit()` at the end of a function after all sensitive operations are completed and before returning. -- Ensure the state variable used for tracking reentrancy is unique to the function or context being protected. +- Always call `enter()` at the beginning of a function that should not be reentrant. +- Always call `exit()` at the end of such a function, ensuring it is called even if an error occurs within the protected block. +- Use the `Reentrancy` custom error for clear and gas-efficient error handling. ## Integration Notes -The `LibNonReentrancy` library operates on a `uint256` storage slot. Facets should use a dedicated `uint256` variable within their own storage to manage the non-reentrancy state for specific functions. The `enter()` function will revert if reentrancy is detected, ensuring that the function's execution is atomic and safe. The `exit()` function is crucial for resetting the state after successful execution. +The `LibNonReentrancy` library manages its state within a `NonReentrancyStorage` struct. This struct should be declared and initialized in the facet that utilizes the library. The `enter` and `exit` functions operate on this storage, ensuring that reentrancy protection is specific to the facet's instance of the storage. The library itself does not introduce new diamond storage slots; it relies on the facet to manage its own storage.
- + diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx index 2ebbc3b4..eae3deae 100644 --- a/website/docs/library/utils/index.mdx +++ b/website/docs/library/utils/index.mdx @@ -11,4 +11,12 @@ import Icon from '@site/src/components/ui/Icon'; Utility libraries and helpers for diamond development. -_No items in this category yet._ + + } + size="medium" + /> + From c62e8e277629a675cc2190b199aa10ef68d9f130 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sun, 21 Dec 2025 21:54:35 +0000 Subject: [PATCH 52/68] docs: auto-generate docs pages from NatSpec --- .../library/diamond/DiamondInspectFacet.mdx | 177 ++++++++++++++++++ website/docs/library/diamond/index.mdx | 7 + 2 files changed, 184 insertions(+) create mode 100644 website/docs/library/diamond/DiamondInspectFacet.mdx diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx new file mode 100644 index 00000000..a70aa6fa --- /dev/null +++ b/website/docs/library/diamond/DiamondInspectFacet.mdx @@ -0,0 +1,177 @@ +--- +sidebar_position: 99 +title: "DiamondInspectFacet" +description: "Inspect diamond storage and facet mappings" +gitSource: "https://github.com/maxnorm/Compose/blob/7e2dd824639f424be644c4ebbb3ca2508167329f/src/diamond/DiamondInspectFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Inspect diamond storage and facet mappings + + + +- Retrieves raw diamond storage bytes directly from its storage slot. +- Maps function selectors to their corresponding facet addresses. +- Enables external inspection of diamond's internal structure. + + +## Overview + +The DiamondInspectFacet provides read-only access to the diamond's internal state and function-to-facet mappings. It allows external entities to query the diamond's storage layout and understand which facets handle specific function selectors without needing direct storage access. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### FunctionFacetPair + + +{`struct FunctionFacetPair { + bytes4 selector; + address facet; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Retrieves the diamond's storage struct from its fixed position. Uses inline assembly to access the storage slot directly. + + +{`function getStorage() internal pure returns (DiamondStorage storage s);`} + + +**Returns:** + + + +--- +### functionFacetPairs + +Returns an array of all function selectors and their corresponding facet addresses. Iterates through the diamond's stored selectors and pairs each with its facet. + + +{`function functionFacetPairs() external view returns (FunctionFacetPair[] memory pairs);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondInspectFacet} from "@compose/contracts/facets/DiamondInspect/IDiamondInspectFacet.sol"; +import {DiamondProxy} from "@compose/contracts/DiamondProxy.sol"; + +contract ConsumerFacet { + IDiamondInspectFacet immutable diamondInspectFacet; + + constructor(address diamondProxyAddress) { + diamondInspectFacet = IDiamondInspectFacet(diamondProxyAddress); + } + + function inspectDiamond() external view returns (bytes[] memory, address[]) { + // Example: Get all function selectors and their facet addresses + (bytes[] memory selectors, address[] memory facets) = diamondInspectFacet.functionFacetPairs(); + return (selectors, facets); + } + + function viewStorage() external view returns (bytes memory) { + // Example: Retrieve the diamond's storage struct + return diamondInspectFacet.getStorage(); + } +}`} + + +## Best Practices + + +- Integrate this facet to provide transparency into diamond functionality and state. +- Use `functionFacetPairs` to map function selectors to their respective implementation contracts for auditing and debugging. +- Access `getStorage` cautiously, understanding it returns raw storage bytes and requires careful deserialization based on the diamond's storage layout. + + +## Security Considerations + + +This facet is read-only and does not modify state, mitigating reentrancy risks. However, `getStorage` returns raw bytes; incorrect deserialization could lead to misinterpretation of the diamond's state. Ensure the consumer understands the diamond's storage layout. + + +
+ +
+ + diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx index 0ef75825..afceb81f 100644 --- a/website/docs/library/diamond/index.mdx +++ b/website/docs/library/diamond/index.mdx @@ -33,6 +33,13 @@ import Icon from '@site/src/components/ui/Icon'; icon={} size="medium" /> + } + size="medium" + /> Date: Sun, 21 Dec 2025 17:02:50 -0500 Subject: [PATCH 53/68] improve struct parsing --- .../templates/templates.js | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index e1cd3639..9931f20c 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -349,6 +349,88 @@ function prepareErrorData(error) { }; } +/** + * Normalize struct definition indentation + * Ensures consistent 4-space indentation for struct body content + * @param {string} definition - Struct definition code + * @returns {string} Normalized struct definition with proper indentation + */ +function normalizeStructIndentation(definition) { + if (!definition) return definition; + + const lines = definition.split('\n'); + if (lines.length === 0) return definition; + + // Find the struct opening line (contains "struct" keyword) + let structStartIndex = -1; + let openingBraceOnSameLine = false; + + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes('struct')) { + structStartIndex = i; + openingBraceOnSameLine = lines[i].includes('{'); + break; + } + } + + if (structStartIndex === -1) return definition; + + // Get the indentation of the struct declaration line + const structLine = lines[structStartIndex]; + const structIndentMatch = structLine.match(/^(\s*)/); + const structIndent = structIndentMatch ? structIndentMatch[1] : ''; + + // Normalize all lines + const normalized = []; + let inStructBody = openingBraceOnSameLine; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + if (i === structStartIndex) { + // Keep struct declaration line as-is + normalized.push(line); + if (openingBraceOnSameLine) { + inStructBody = true; + } + continue; + } + + // Handle opening brace on separate line + if (!openingBraceOnSameLine && trimmed === '{') { + normalized.push(structIndent + '{'); + inStructBody = true; + continue; + } + + // Handle closing brace + if (trimmed === '}') { + normalized.push(structIndent + '}'); + inStructBody = false; + continue; + } + + // Skip empty lines + if (trimmed === '') { + normalized.push(''); + continue; + } + + // For struct body content, ensure 4-space indentation relative to struct declaration + if (inStructBody) { + // Remove any existing indentation and add proper indentation + const bodyIndent = structIndent + ' '; // 4 spaces + normalized.push(bodyIndent + trimmed); + } else { + // Keep lines outside struct body as-is + normalized.push(line); + } + } + + return normalized.join('\n'); +} + /** * Prepare struct data for template rendering * @param {object} struct - Struct data @@ -358,7 +440,7 @@ function prepareStructData(struct) { return { name: struct.name, description: struct.description || '', - definition: struct.definition, + definition: normalizeStructIndentation(struct.definition), }; } From 4cc421d044608966ebcab173215b892a09131bb7 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Sun, 21 Dec 2025 22:08:25 +0000 Subject: [PATCH 54/68] docs: auto-generate docs pages from NatSpec --- .../AccessControl/AccessControlFacet.mdx | 86 ++++++++++--------- .../access/AccessControl/AccessControlMod.mdx | 63 +++++++------- .../library/access/AccessControl/index.mdx | 4 +- .../AccessControlPausableFacet.mdx | 68 +++++++++++++-- .../AccessControlPausableMod.mdx | 50 ++++++----- .../access/AccessControlPausable/index.mdx | 4 +- .../AccessControlTemporalFacet.mdx | 80 ++++++++--------- .../AccessControlTemporalMod.mdx | 46 ++++++---- .../access/AccessControlTemporal/index.mdx | 2 +- .../docs/library/access/Owner/OwnerFacet.mdx | 59 +++++++++++-- .../docs/library/access/Owner/OwnerMod.mdx | 52 +++++------ website/docs/library/access/Owner/index.mdx | 2 +- .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 39 ++++----- .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 57 +++++------- .../docs/library/diamond/DiamondCutFacet.mdx | 60 +++++++------ .../docs/library/diamond/DiamondCutMod.mdx | 80 ++++++++--------- .../library/diamond/DiamondInspectFacet.mdx | 53 ++++++------ .../library/diamond/DiamondLoupeFacet.mdx | 44 +++++----- website/docs/library/diamond/DiamondMod.mdx | 59 ++++++------- .../diamond/example/ExampleDiamond.mdx | 57 ++++++------ .../docs/library/diamond/example/index.mdx | 2 +- website/docs/library/diamond/index.mdx | 10 +-- .../interfaceDetection/ERC165/ERC165Mod.mdx | 56 +++++------- .../interfaceDetection/ERC165/index.mdx | 2 +- .../library/token/ERC1155/ERC1155Facet.mdx | 58 ++++++------- .../docs/library/token/ERC1155/ERC1155Mod.mdx | 53 ++++++------ website/docs/library/token/ERC1155/index.mdx | 4 +- .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 44 ++++++---- .../library/token/ERC20/ERC20/ERC20Facet.mdx | 46 +++++----- .../library/token/ERC20/ERC20/ERC20Mod.mdx | 70 +++++++-------- .../docs/library/token/ERC20/ERC20/index.mdx | 6 +- .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 59 ++++++------- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 45 +++++----- .../token/ERC20/ERC20Bridgeable/index.mdx | 4 +- .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 66 +++++++++----- .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 74 +++++++--------- .../library/token/ERC20/ERC20Permit/index.mdx | 4 +- .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 48 ++++++----- .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 69 +++++++++------ .../library/token/ERC6909/ERC6909/index.mdx | 4 +- .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 49 ++++++----- .../token/ERC721/ERC721/ERC721Facet.mdx | 40 ++++----- .../library/token/ERC721/ERC721/ERC721Mod.mdx | 64 ++++++++------ .../library/token/ERC721/ERC721/index.mdx | 6 +- .../ERC721EnumerableBurnFacet.mdx | 38 ++++---- .../ERC721EnumerableFacet.mdx | 48 +++++------ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 69 ++++++++------- .../token/ERC721/ERC721Enumerable/index.mdx | 6 +- .../library/token/Royalty/RoyaltyFacet.mdx | 55 +++++------- .../docs/library/token/Royalty/RoyaltyMod.mdx | 59 ++++++------- website/docs/library/token/Royalty/index.mdx | 2 +- .../docs/library/utils/NonReentrancyMod.mdx | 55 +++++++----- website/docs/library/utils/index.mdx | 2 +- 53 files changed, 1126 insertions(+), 1056 deletions(-) diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx index d8c281fe..8c6166a4 100644 --- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlFacet" -description: "Manages roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControl/AccessControlFacet.sol" +description: "Manage roles and permissions within the diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControl/AccessControlFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages roles and permissions within a diamond. +Manage roles and permissions within the diamond. -- Role-based access control (RBAC) system. -- Granular permission management through roles and accounts. -- Support for granting, revoking, and checking roles. -- Admin role hierarchy for role management. +- Role-based access control system. +- Supports granting and revoking roles for individual accounts or batches. +- Provides functions to check role membership and enforce role requirements. ## Overview -The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling developers to define roles, assign them to accounts, and enforce access restrictions on functions. This facet is crucial for orchestrating secure interactions within the diamond. +The AccessControlFacet provides a robust role-based access control system for your Compose diamond. It allows for granular permission management, enabling you to define administrative roles and grant specific privileges to accounts. This facet ensures that sensitive operations are performed only by authorized entities, enhancing the security and integrity of your diamond. --- @@ -500,35 +499,42 @@ error AccessControlUnauthorizedSender(address _sender, address _account); {`pragma solidity ^0.8.30; -import {DiamondLoupeFacet} from "@compose-protocol/diamond/facets/DiamondLoupe/DiamondLoupeFacet.sol"; -import {AccessControlFacet} from "@compose-protocol/diamond/facets/AccessControl/AccessControlFacet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond/contracts/interfaces/IDiamondCut.sol"; - -contract DeployAccessControl { - // Assume diamondAddress is the address of your deployed diamond proxy - address diamondAddress; - - function deploy() external { - // ... deployment logic ... - - // Example: Granting ROLE_ADMIN to the deployer - AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress); - bytes32 adminRole = accessControlFacet.getRoleAdmin(keccak256("ROLE_USER")); - accessControlFacet.grantRole(adminRole, msg.sender); - - // Example: Revoking ROLE_USER from a specific account - address userToRevoke = address(0x123); - accessControlFacet.revokeRole(keccak256("ROLE_USER"), userToRevoke); - } - - function checkPermission(address _account, bytes32 _role) public view returns (bool) { - AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress); - return accessControlFacet.hasRole(_role, _account); +import {DiamondCutFacet} from "@compose/diamond-cut/src/DiamondCutFacet.sol"; +import {AccessControlFacet} from "@compose/access-control/src/AccessControlFacet.sol"; + +contract MyDiamond is DiamondInit { + + function upgrade() public { + DiamondCutFacet diamondCutFacet = DiamondCutFacet(address(this)); + AccessControlFacet accessControlFacet = new AccessControlFacet(); + + diamondCutFacet.diamondCut([ + FacetCut({ + facetAddress: address(accessControlFacet), + action: FacetCutAction.ADD, + functionSelectors: + AccessControlFacet.getStorage.selector + | AccessControlFacet.hasRole.selector + | AccessControlFacet.requireRole.selector + | AccessControlFacet.getRoleAdmin.selector + | AccessControlFacet.setRoleAdmin.selector + | AccessControlFacet.grantRole.selector + | AccessControlFacet.revokeRole.selector + | AccessControlFacet.grantRoleBatch.selector + | AccessControlFacet.revokeRoleBatch.selector + | AccessControlFacet.renounceRole.selector + }) + ], address(0), ""); + + // Initialize Access Control + address accessControlAddress = diamondCutFacet.getFacetAddress(AccessControlFacet.getStorage.selector); + AccessControlFacet(accessControlAddress).grantRole(AccessControlFacet.DEFAULT_ADMIN_ROLE(), msg.sender); } - function enforcePermission(address _account, bytes32 _role) public view { - AccessControlFacet accessControlFacet = AccessControlFacet(diamondAddress); - accessControlFacet.requireRole(_role, _account); + function checkPermission() public view { + address accessControlAddress = diamondCutFacet.getFacetAddress(AccessControlFacet.getStorage.selector); + AccessControlFacet(accessControlAddress).requireRole(AccessControlFacet.DEFAULT_ADMIN_ROLE(), msg.sender); + // ... perform privileged operation ... } }`} @@ -536,19 +542,19 @@ contract DeployAccessControl { ## Best Practices -- Define roles as `bytes32` constants for clarity and consistency. -- Use batch functions (`grantRoleBatch`, `revokeRoleBatch`) for efficiency when managing multiple accounts for the same role. -- Carefully manage the `ROLE_ADMIN` role, as it controls the ability to modify role assignments. +- Grant the `DEFAULT_ADMIN_ROLE` only to trusted deployer or initial admin addresses. +- Use `grantRoleBatch` and `revokeRoleBatch` for efficiency when managing multiple accounts for a single role. +- Regularly audit role assignments to ensure adherence to the principle of least privilege. ## Security Considerations -Ensure that the caller has the necessary permissions to grant or revoke roles. Unauthorized calls to `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch` will revert. The `renounceRole` function can only be called by the account requesting to renounce the role. +Ensure that the `DEFAULT_ADMIN_ROLE` is securely managed, as it has the power to grant or revoke any role. Reentrancy is not a concern as most functions perform state changes and then emit events without external calls. Input validation is handled internally by the facet to prevent invalid role or account assignments.
- + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx index 8bc65da2..497cae93 100644 --- a/website/docs/library/access/AccessControl/AccessControlMod.mdx +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlMod" -description: "Manage roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControl/AccessControlMod.sol" +description: "Manage role-based access control within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControl/AccessControlMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,7 +21,7 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage roles and permissions within a diamond. +Manage role-based access control within a diamond. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The AccessControl module provides a robust framework for managing role-based access control within Compose diamonds. It enables granular permission management, ensuring that only authorized accounts can execute specific functions. This module is crucial for securing diamond functionality and maintaining a clear separation of administrative privileges. +This module provides a robust framework for implementing role-based access control (RBAC) within Compose diamonds. It allows for granular permission management by assigning roles to accounts, ensuring that only authorized users can execute specific functions. By adhering to Compose's storage pattern, it integrates seamlessly with diamond upgrades. --- @@ -46,8 +46,8 @@ The AccessControl module provides a robust framework for managing role-based acc {`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; }`} @@ -401,34 +401,31 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlMod} from "@compose/diamond/modules/access-control/IAccessControlMod.sol"; +import {IAccessControlMod} from "@compose/modules/access-control/IAccessControlMod.sol"; contract MyFacet { - IAccessControlMod internal accessControl; - - // Assuming accessControl is initialized externally and its storage slot is known - constructor(address _diamondProxy, bytes32 _accessControlStorageSlot) { - accessControl = IAccessControlMod(_diamondProxy); - // To interact with the AccessControl module, you would typically call its functions directly - // via the diamond proxy, which routes calls to the correct facet. The storage slot is - // generally not directly used by facets but is relevant for deployment and upgrades. + IAccessControlMod internal accessControlMod; + + constructor(address _accessControlModAddress) { + accessControlMod = IAccessControlMod(_accessControlModAddress); } - /** - * @notice Grants the DEFAULT_ADMIN_ROLE to the caller. - */ - function initialize() external { - bytes32 adminRole = accessControl.getStorage().DEFAULT_ADMIN_ROLE; - accessControl.grantRole(adminRole, msg.sender); + function someRestrictedFunction() external { + address sender = msg.sender; + bytes32 ownerRole = keccak256("OWNER_ROLE"); + + accessControlMod.requireRole(ownerRole, sender); + + // ... execute restricted logic ... } - /** - * @notice Requires the caller to have the MINTER_ROLE. - */ - function mintToken(address _to, uint256 _amount) external { - bytes32 minterRole = accessControl.getStorage().MINTER_ROLE; // Assuming MINTER_ROLE is defined - accessControl.requireRole(minterRole, msg.sender); - // ... minting logic ... + function grantOwnerRole(address _account) external { + address sender = msg.sender; + bytes32 ownerRole = keccak256("OWNER_ROLE"); + bytes32 adminRole = keccak256("DEFAULT_ADMIN_ROLE"); + + accessControlMod.requireRole(adminRole, sender); + accessControlMod.grantRole(ownerRole, _account); } }`} @@ -436,19 +433,19 @@ contract MyFacet { ## Best Practices -- Use `requireRole` to enforce access control checks at the beginning of sensitive functions. -- Carefully manage role assignments and revocations, especially for administrative roles. -- Understand that role changes are state-changing operations and should be audited. +- Use `requireRole` to enforce access control checks at the beginning of sensitive functions, reverting with `AccessControlUnauthorizedAccount` if the caller lacks the necessary role. +- Define roles using `keccak256` hashes for clarity and gas efficiency. +- Manage role administration carefully by setting appropriate `DEFAULT_ADMIN_ROLE` and using `setRoleAdmin` to control role hierarchy. ## Integration Notes -The AccessControl module manages its state within a dedicated storage slot. Facets interact with the module's functionality by calling its functions through the diamond proxy. The `getStorage()` function provides read access to the module's internal storage structure. Any changes to roles made via `grantRole`, `revokeRole`, or `setRoleAdmin` are persistent and visible to all facets interacting with the diamond. +The `AccessControlMod` utilizes its own storage slot within the diamond's storage layout. Facets interact with this module through its interface (`IAccessControlMod`). Functions like `grantRole`, `revokeRole`, and `hasRole` directly read from and write to the module's storage. The `requireRole` function acts as a guard, reverting execution if the calling account does not possess the specified role. Ensure the `AccessControlMod` is initialized correctly during the diamond deployment process.
- + diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx index 34964496..3c1dc511 100644 --- a/website/docs/library/access/AccessControl/index.mdx +++ b/website/docs/library/access/AccessControl/index.mdx @@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx index f4872463..c375d08a 100644 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlPausableFacet" -description: "Access Control Pausable facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +description: "Manage roles and pausing functionality within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlPausable/AccessControlPausableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Access Control Pausable facet for Compose diamonds +Manage roles and pausing functionality within a diamond. -- Self-contained facet with no imports or inheritance -- Only `external` and `internal` function visibility -- Follows Compose readability-first conventions -- Ready for diamond integration +- Role-specific pausing and unpausing capabilities. +- Integration with existing diamond access control mechanisms. +- Prevents execution of role-restricted functions when a role is paused. ## Overview -Access Control Pausable facet for Compose diamonds +This facet provides granular control over role-based access and the ability to temporarily pause specific roles. It integrates with the diamond's access control system to enforce role restrictions and prevent execution when roles are paused, enhancing security and operational flexibility. --- @@ -322,8 +321,59 @@ error AccessControlRolePaused(bytes32 _role); +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableFacet} from "@compose/diamond/facets/AccessControlPausable/IAccessControlPausableFacet.sol"; + +contract ExampleUsage { + IAccessControlPausableFacet accessControlPausableFacet; + + constructor(address _diamondAddress) { + // Assume accessControlPausableFacet is a registered facet on the diamond + accessControlPausableFacet = IAccessControlPausableFacet(_diamondAddress); + } + + function checkRolePaused(bytes32 _role) public view { + bool paused = accessControlPausableFacet.isRolePaused(_role); + // Use 'paused' variable + } + + function pauseMyRole(bytes32 _role) public { + // Caller must be the admin of _role + accessControlPausableFacet.pauseRole(_role); + } + + function unpauseMyRole(bytes32 _role) public { + // Caller must be the admin of _role + accessControlPausableFacet.unpauseRole(_role); + } + + function ensureRoleActive(bytes32 _role) public { + // Reverts if caller is not authorized for _role or if _role is paused + accessControlPausableFacet.requireRoleNotPaused(_role); + } +}`} + + +## Best Practices + + +- Ensure the `AccessControlPausableFacet` is correctly initialized and its functions are accessible via the diamond proxy. +- Grant the `PAUSER_ROLE` and `ROLE_ADMIN_ROLE` (or equivalent roles defined by your access control implementation) judiciously, as they control pausing and unpausing operations. +- Use `requireRoleNotPaused` proactively within other facets to prevent execution when a role's functionality is temporarily suspended. + + +## Security Considerations + + +Access to `pauseRole` and `unpauseRole` functions is restricted to the administrative role of the specific role being managed. Ensure that the administrative roles are themselves secured appropriately. Reentrancy is not a direct concern for `pauseRole` and `unpauseRole` as they only modify state, but calls within other facets that rely on `requireRoleNotPaused` should be audited for reentrancy risks. + +
- + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx index 0ded9969..9b4540eb 100644 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlPausableMod" -description: "Manage role pausing and unpausing for access control." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlPausable/AccessControlPausableMod.sol" +description: "Manages role-based access control with pausing capabilities." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlPausable/AccessControlPausableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage role pausing and unpausing for access control. +Manages role-based access control with pausing capabilities. -- Allows granular pausing and unpausing of individual roles. -- Provides a `requireRoleNotPaused` check that reverts if the role is paused or the caller lacks the role. -- Integrates with existing access control mechanisms. +- Role-specific pausing: Temporarily disable functionality tied to specific roles. +- Permission enforcement: `requireRoleNotPaused` checks both role membership and pause status. +- Diamond storage integration: Leverages the diamond's storage for persistent pause states. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides functionality to pause and unpause specific roles within an access control system. By pausing a role, all operations requiring that role are temporarily blocked, enhancing safety during upgrades or critical operations. It ensures that sensitive actions are only permitted when the relevant role is active. +This module enhances role-based access control by introducing the ability to pause specific roles. This allows for temporarily halting operations associated with a role without revoking permissions. It integrates seamlessly with the diamond's storage pattern, making paused states visible and manageable across facets. --- @@ -46,7 +46,7 @@ This module provides functionality to pause and unpause specific roles within an {`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; + mapping(bytes32 role => bool paused) pausedRoles; }`} @@ -55,8 +55,8 @@ mapping(bytes32 role => bool paused) pausedRoles; {`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; }`} @@ -330,24 +330,22 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlPausableMod} from "@compose/modules/access-control/pausable/IAccessControlPausableMod.sol"; +import {IAccessControlPausableMod} from "./interfaces/IAccessControlPausableMod.sol"; contract MyFacet { - IAccessControlPausableMod accessControlPausableMod; + IAccessControlPausableMod public constant ACCESS_CONTROL_PAUSABLE_MOD = IAccessControlPausableMod(
); - constructor(address _accessControlPausableMod) { - accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); + function _someProtectedAction() internal view { + ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(msg.sender, IAccessControlBase.Role.OPERATOR); + // ... proceed with action } - function grantRoleAndPause(bytes32 role, address account) external { - // Assume role granting logic exists elsewhere or is handled by another facet - accessControlPausableMod.pauseRole(role); + function _pauseOperatorRole() external { + ACCESS_CONTROL_PAUSABLE_MOD.pauseRole(IAccessControlBase.Role.OPERATOR); } - function unpauseRoleAndPerformAction(bytes32 role) external { - accessControlPausableMod.unpauseRole(role); - // Perform action only allowed when role is not paused - accessControlPausableMod.requireRoleNotPaused(role, msg.sender); + function _unpauseOperatorRole() external { + ACCESS_CONTROL_PAUSABLE_MOD.unpauseRole(IAccessControlBase.Role.OPERATOR); } }`} @@ -355,19 +353,19 @@ contract MyFacet { ## Best Practices -- Use `requireRoleNotPaused` to enforce that a role must be active before executing sensitive operations. -- Grant roles before pausing them to ensure continuity of access control management. -- Always unpause roles when operations are intended to resume. +- Use `requireRoleNotPaused` to enforce role presence and ensure the role is not currently paused before executing sensitive actions. +- Implement pausing and unpausing logic carefully, ensuring only authorized entities can call these functions. +- Be aware that pausing a role affects all facets that rely on that role's active status. ## Integration Notes -The `AccessControlPausableMod` module manages its state in separate storage slots, distinct from the core Access Control storage. Facets can access this state via the provided getter functions (`getAccessControlStorage`, `getStorage`). The `requireRoleNotPaused` function acts as a critical guard, ensuring that operations tied to a specific role are only executable when that role is not in a paused state. This module does not alter the fundamental role granting or revoking mechanisms of the underlying Access Control facet. +This module interacts with the diamond's storage to track the paused status of roles. Facets can query `isRolePaused` or use `requireRoleNotPaused` to ensure operations are permitted. The `AccessControlPausableMod` contract is expected to reside at a known address within the diamond's facet registry. Changes to role pause states are immediately reflected across all interacting facets.
- + diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx index a1c933e1..9edc2a1e 100644 --- a/website/docs/library/access/AccessControlPausable/index.mdx +++ b/website/docs/library/access/AccessControlPausable/index.mdx @@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx index 4e6427cf..c87ba94d 100644 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments and their validation." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +description: "Manages time-bound role assignments within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages time-bound role assignments and their validation. +Manages time-bound role assignments within a Compose diamond. -- Grants roles with specific expiration timestamps. -- Provides functions to check if a role has expired or is currently valid. -- Restricts role granting and revocation to the role's admin. +- Time-bound role assignments: Roles expire automatically after a specified timestamp. +- Admin-controlled granting and revoking: Only role administrators can manage temporal roles. +- Explicit expiry checks: Functions to check if a role has expired and to enforce valid, non-expired roles. ## Overview -This facet extends the standard Access Control pattern by introducing time-bound roles. It allows administrators to grant roles with specific expiry timestamps and provides mechanisms to check role validity, ensuring that access is automatically revoked when a role expires. This is crucial for managing temporary permissions within a Compose diamond. +This facet extends Compose's access control by introducing time-limited role assignments. It allows administrators to grant roles that automatically expire, enhancing granular control over permissions. The facet provides mechanisms to grant, revoke, and verify the validity of these time-bound roles. --- @@ -403,46 +403,34 @@ error AccessControlRoleExpired(bytes32 _role, address _account); {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; -import {AccessControlTemporalFacet} from "src/facets/AccessControlTemporalFacet.sol"; +import {IComposeDiamond} from "@compose-protocol/diamond-contracts/contracts/diamond/IComposeDiamond.sol"; +import {AccessControlTemporalFacet} from "@compose-protocol/diamond-contracts/contracts/facets/AccessControlTemporalFacet.sol"; -contract DeployExample { +contract Deployer { address public diamondAddress; - function deployDiamond() public { - // ... deployment logic for diamond proxy ... - diamondAddress = address(0x123); // Replace with actual diamond address - - // Add AccessControlTemporalFacet - bytes32[] memory selectors = new bytes32[](7); - selectors[0] = AccessControlTemporalFacet.grantRoleWithExpiry.selector; - selectors[1] = AccessControlTemporalFacet.revokeTemporalRole.selector; - selectors[2] = AccessControlTemporalFacet.getRoleExpiry.selector; - selectors[3] = AccessControlTemporalFacet.isRoleExpired.selector; - selectors[4] = AccessControlTemporalFacet.requireValidRole.selector; - selectors[5] = AccessControlTemporalFacet.getAccessControlStorage.selector; - selectors[6] = AccessControlTemporalFacet.getStorage.selector; - - IDiamondCut(diamondAddress).diamondCut( - IDiamondCut.FacetCut[]( - IDiamondCut.FacetCut({ - facetAddress: address(new AccessControlTemporalFacet()), - action: IDiamondCut.FacetCutAction.ADD, - isEdgeCase: false, - selectors: selectors - }) - ), - address(0), // init contract - '()' // init data + function deploy() public { + // Assume diamondAddress is already set and the facet is added + diamondAddress = address(0x123...); // Replace with actual diamond address + + IComposeDiamond diamond = IComposeDiamond(diamondAddress); + + // Example: Grant 'PAUSER' role to address(0x456...) for 1 hour from now + uint256 expiryTimestamp = block.timestamp + 1 hours; + bytes32 PAUSER_ROLE = keccak256("PAUSER"); + diamond.callFacetFunction(AccessControlTemporalFacet.getFunctionSelector("grantRoleWithExpiry(bytes32,address,uint256)"), + abi.encodeCall(AccessControlTemporalFacet.grantRoleWithExpiry, + (PAUSER_ROLE, address(0x456...), expiryTimestamp) + ) ); - } - function grantTemporaryRole(address account, bytes32 role, uint64 expiry) public { - AccessControlTemporalFacet(diamondAddress).grantRoleWithExpiry(role, account, expiry); - } + // Example: Check if a role is expired + bool expired = AccessControlTemporalFacet(diamondAddress).isRoleExpired(PAUSER_ROLE, address(0x456...)); - function checkRole(address account, bytes32 role) public { - AccessControlTemporalFacet(diamondAddress).requireValidRole(role, account); + // Example: Require a valid role (will revert if not valid or expired) + try AccessControlTemporalFacet(diamondAddress).requireValidRole(PAUSER_ROLE, address(0x456...)) { + // Role is valid and not expired + } catch AccessControlTemporalFacet.AccessControlRoleExpired {} catch AccessControlTemporalFacet.AccessControlUnauthorizedAccount {} } }`} @@ -450,19 +438,19 @@ contract DeployExample { ## Best Practices -- Ensure the AccessControl facet is deployed and configured before adding this temporal facet. -- Grant roles with expiry only to trusted accounts and set appropriate expiry timestamps. -- Utilize `requireValidRole` within other facets to enforce time-bound access control checks. +- Grant roles with expiry only to trusted accounts and with appropriate lifespans. +- Regularly monitor role assignments to ensure they align with current operational needs. +- Utilize `requireValidRole` within your facet logic to enforce time-bound access control checks. ## Security Considerations -Access control is enforced by the role's admin. Ensure the admin role itself is secured. The `grantRoleWithExpiry` function reverts if the caller is not the admin of the specified role. The `revokeTemporalRole` function also requires the caller to be the admin. `requireValidRole` correctly reverts with `AccessControlRoleExpired` if the role has passed its expiry timestamp. +The `grantRoleWithExpiry` and `revokeTemporalRole` functions are restricted to the admin of the respective role, preventing unauthorized modifications. Ensure that the admin role itself is adequately secured. The `requireValidRole` function prevents the use of expired roles, mitigating risks associated with stale permissions. Reentrancy is not a concern as these functions do not make external calls.
- + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx index 1d2ee032..9e0ee783 100644 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "AccessControlTemporalMod" description: "Manage time-bound role assignments in Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,9 +25,9 @@ Manage time-bound role assignments in Compose diamonds. -- Grants roles with a specific expiry timestamp. -- Automatically checks for role expiry upon validation. -- Allows explicit revocation of temporal roles. +- Grants roles with configurable expiry timestamps. +- Automatically enforces role validity, revoking expired assignments. +- Provides functions to check role expiry and revoke temporal roles. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module extends standard access control by introducing time-bound role assignments. It allows granting roles that automatically expire, enhancing security and operational flexibility. By integrating temporal logic, diamonds can enforce dynamic permissions, reducing the need for manual role revocation. +This module extends standard access control by introducing time-bound role assignments. It allows for roles to be granted with a specific expiry timestamp, automatically revoking them once that time passes. This enhances security and automates the cleanup of temporary permissions. --- @@ -46,8 +46,8 @@ This module extends standard access control by introducing time-bound role assig {`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; }`} @@ -56,7 +56,7 @@ mapping(bytes32 role => bytes32 adminRole) adminRole; {`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; }`} @@ -433,21 +433,29 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlTemporalMod} from "@compose/diamond-protocol/src/modules/accesscontroltemporal/IAccessControlTemporalMod.sol"; +import {IAccessControlTemporalMod} from "@compose/contracts/modules/AccessControlTemporalMod.sol"; +import {IDiamond} from "@compose/contracts/IDiamond.sol"; contract MyDiamondFacet { IAccessControlTemporalMod internal accessControlTemporalMod; - function grantRoleTemporarily(address _account, bytes32 _role, uint64 _expiry) external { - accessControlTemporalMod.grantRoleWithExpiry(_account, _role, _expiry); + function initialize(IDiamond _diamond) external { + accessControlTemporalMod = IAccessControlTemporalMod(_diamond.getFacetAddress(address(this))); } - function checkRoleValidity(address _account, bytes32 _role) external view { - accessControlTemporalMod.requireValidRole(_account, _role); + function grantTempAdmin(address _account, uint64 _expiry) external { + accessControlTemporalMod.grantRoleWithExpiry( + keccak256("ADMIN_ROLE"), + _account, + _expiry + ); } - function revokeRole(address _account, bytes32 _role) external { - accessControlTemporalMod.revokeTemporalRole(_account, _role); + function checkAdminStatus(address _account) external view { + accessControlTemporalMod.requireValidRole( + keccak256("ADMIN_ROLE"), + _account + ); } }`} @@ -456,18 +464,18 @@ contract MyDiamondFacet { - Use `requireValidRole` to enforce non-expired role checks before sensitive operations. -- Grant roles with explicit expiry timestamps to minimize stale permissions. -- Regularly audit temporal role assignments to ensure continued necessity. +- Carefully manage expiry timestamps to prevent accidental role expiration or prolonged access. +- Leverage custom errors `AccessControlRoleExpired` and `AccessControlUnauthorizedAccount` for clear revert reasons. ## Integration Notes -The AccessControlTemporalMod manages its own storage, distinct from other facets. When integrating, ensure its storage slot is correctly mapped. The `requireValidRole` function directly interacts with diamond access control logic, potentially reverting with `AccessControlUnauthorizedAccount` or `AccessControlRoleExpired`. +This module interacts with the diamond's storage. Facets can access its functionality via the diamond proxy. Ensure the `AccessControlTemporalMod` facet is correctly initialized and accessible. The module manages role assignments and their expiry, which is critical for any access control logic within other facets.
- + diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx index 1ac6b9b1..0f1a7968 100644 --- a/website/docs/library/access/AccessControlTemporal/index.mdx +++ b/website/docs/library/access/AccessControlTemporal/index.mdx @@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx index 4697bbe5..95205243 100644 --- a/website/docs/library/access/Owner/OwnerFacet.mdx +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerFacet" -description: "Owner facet for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/Owner/OwnerFacet.sol" +description: "Manages contract ownership and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/Owner/OwnerFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Owner facet for Compose diamonds +Manages contract ownership and transfers. -- Self-contained facet with no imports or inheritance -- Only `external` and `internal` function visibility -- Follows Compose readability-first conventions -- Ready for diamond integration +- Standard ERC-173 ownership pattern implementation. +- Provides explicit functions for owner retrieval, transfer, and renunciation. +- Supports ownership transfer to `address(0)` for renunciation. ## Overview -Owner facet for Compose diamonds +The OwnerFacet provides standard ownership management functions for a Compose diamond. It allows for the retrieval of the current owner, the transfer of ownership to a new address, and the renunciation of ownership. This facet is crucial for controlling administrative functions within the diamond. --- @@ -163,8 +162,50 @@ error OwnerUnauthorizedAccount(); +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "@compose/facets/owner/IOwnerFacet.sol"; + +contract OwnerConsumer { + IOwnerFacet public ownerFacet; + + constructor(address _ownerFacetAddress) { + ownerFacet = IOwnerFacet(_ownerFacetAddress); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); + } + + function transferDiamondOwnership(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function renounceDiamondOwnership() external { + ownerFacet.renounceOwnership(); + } +}`} + + +## Best Practices + + +- Initialize ownership during diamond deployment to a trusted address. +- Only transfer ownership to addresses that are prepared to manage the diamond's administrative functions. +- Use `renounceOwnership` with extreme caution, as it permanently removes the owner. + + +## Security Considerations + + +Ownership is a critical administrative role. Ensure that ownership transfers are authorized and that the new owner is a secure and trusted address. The `transferOwnership` function can be used to renounce ownership by setting the `_newOwner` to `address(0)`. Unauthorized access to ownership transfer functions could lead to a loss of control over the diamond. + +
- + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx index 93984ecb..9f0a3b97 100644 --- a/website/docs/library/access/Owner/OwnerMod.mdx +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "OwnerMod" description: "Manages contract ownership according to ERC-173." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/Owner/OwnerMod.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/Owner/OwnerMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,10 +25,10 @@ Manages contract ownership according to ERC-173. -- ERC-173 compliant ownership tracking. -- `owner()`: Query the current contract owner. -- `transferOwnership(_newOwner)`: Transfer ownership, including renunciation. -- `requireOwner()`: Enforce owner-only access control. +- Implements ERC-173 standard for contract ownership. +- Provides `owner()` view function to retrieve the current owner. +- Includes `requireOwner()` for access control, reverting if the caller is not the owner. +- Supports ownership transfer and renouncement via `transferOwnership()`. @@ -37,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The OwnerMod provides essential functionality for managing contract ownership, adhering to the ERC-173 standard. It enables querying the current owner, transferring ownership, and enforcing owner-only access controls, crucial for secure contract administration within a diamond proxy. +The OwnerMod provides the foundational storage and functions for ERC-173 contract ownership. It enables secure owner management, including transferring ownership and renouncing it. This module is crucial for establishing administrative control within a diamond, ensuring that critical operations can be restricted to authorized entities. --- @@ -49,7 +49,7 @@ storage-location: erc8042:compose.owner {`struct OwnerStorage { -address owner; + address owner; }`} @@ -208,26 +208,28 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerMod} from "@compose/modules/OwnerMod.sol"; -import {DiamondStorage} from "@compose/DiamondStorage.sol"; +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; +import {OwnerStorage} from "@compose/modules/owner/OwnerStorage.sol"; -contract MyFacet { - using DiamondStorage for DiamondStorage; +contract MyOwnerFacet { + // Assuming OwnerMod storage is deployed at STORAGE_POSITION + uint256 constant STORAGE_POSITION = 1; - uint256 constant OWNER_MOD_STORAGE_SLOT = 1; // Example slot, actual value depends on deployment + function getOwner() external view returns (address) { + OwnerStorage storage storageSlot = OwnerStorage(STORAGE_POSITION); + return storageSlot.owner; + } - function _getOwnerMod() internal view returns (IOwnerMod) { - return IOwnerMod(address(this).staticcall(bytes4(keccak256("getStorage(uint256)")), OWNER_MOD_STORAGE_SLOT)); + function transferContractOwnership(address _newOwner) external { + IOwnerMod(msg.sender).transferOwnership(_newOwner); } - function checkOwnership() external view { - address currentOwner = _getOwnerMod().owner(); - // Use owner address + function renounceContractOwnership() external { + IOwnerMod(msg.sender).transferOwnership(address(0)); } - function transferControl(address _newOwner) external { - // Ensure caller has permission to transfer ownership (e.g., is current owner) - _getOwnerMod().transferOwnership(_newOwner); + function requireCallerIsOwner() external view { + IOwnerMod(msg.sender).requireOwner(); } }`} @@ -235,19 +237,19 @@ contract MyFacet { ## Best Practices -- Use `requireOwner()` to restrict sensitive administrative functions to the contract owner. -- Renounce ownership by setting the new owner to `address(0)` to prevent further control. -- Ensure the OwnerMod is initialized with the correct storage slot to avoid conflicts. +- Only the current owner should be able to call `transferOwnership` or renounce ownership. +- Use `transferOwnership` with `address(0)` to safely renounce ownership, preventing accidental lockouts. +- Facets interacting with ownership should explicitly check the owner using `requireOwner` or by reading the owner address. ## Integration Notes -The OwnerMod utilizes a dedicated storage slot (defined by `STORAGE_POSITION`) to store its ownership state. Facets interact with the module via its `IOwnerMod` interface, typically by obtaining a pointer to the module's storage using `getStorage(STORAGE_POSITION)`. Changes to ownership are immediately reflected and observable by all facets. +The OwnerMod utilizes a dedicated storage slot (defined by `STORAGE_POSITION` in `OwnerStorage.sol`) to store the `OwnerStorage` struct. Any facet that needs to interact with ownership functions or check the owner's address must be aware of this storage layout and access it appropriately, typically by referencing the `IOwnerMod` interface and ensuring the module is deployed at the correct slot within the diamond's storage map.
- + diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx index 564c1933..80a2ad8a 100644 --- a/website/docs/library/access/Owner/index.mdx +++ b/website/docs/library/access/Owner/index.mdx @@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx index 826823c8..c4ee03b6 100644 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "OwnerTwoStepsFacet" description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,14 +25,14 @@ Manages contract ownership with a two-step transfer process. -- Two-step ownership transfer: requires initiation and acceptance. -- `renounceOwnership` allows for relinquishing ownership. -- Provides direct access to owner and pending owner addresses. +- Two-step ownership transfer process for enhanced security. +- `owner()` and `pendingOwner()` view functions to check current and proposed owners. +- `renounceOwnership()` function to remove ownership entirely. ## Overview -The OwnerTwoStepsFacet provides a secure, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are deliberate by requiring both the current owner to initiate a transfer and the new owner to accept it, preventing accidental or malicious takeovers. +This facet provides a robust ownership management system for Compose diamonds, enforcing a two-step transfer process to prevent accidental ownership loss. It allows the current owner to initiate a transfer and requires the new owner to explicitly accept it, ensuring secure control over critical diamond functions. --- @@ -242,31 +242,26 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerTwoStepsFacet} from "@compose/core/src/facets/ownership/IOwnerTwoStepsFacet.sol"; +import {IOwnerTwoStepsFacet} from "@compose/facets/OwnerTwoStepsFacet.sol"; -contract Diamond { +contract MyDiamond { IOwnerTwoStepsFacet ownerFacet; - function setOwnerFacet(address _facetAddress) external { - ownerFacet = IOwnerTwoStepsFacet(_facetAddress); - } + // Assume ownerFacet is initialized and its selector is registered - // Example of initiating an ownership transfer - function initiateOwnershipTransfer(address _newOwner) external { - // Assuming Diamond contract has an access control mechanism for this function + function proposeNewOwner(address _newOwner) external { ownerFacet.transferOwnership(_newOwner); } - // Example of accepting ownership transfer (would be called by the new owner) - function acceptOwnershipTransfer() external { + function acceptNewOwnership() external { ownerFacet.acceptOwnership(); } - function getOwner() external view returns (address) { + function getCurrentOwner() external view returns (address) { return ownerFacet.owner(); } - function getPendingOwner() external view returns (address) { + function getPendingOwnerAddress() external view returns (address) { return ownerFacet.pendingOwner(); } }`} @@ -275,19 +270,19 @@ contract Diamond { ## Best Practices -- Initialize the facet with the current owner address during deployment. -- Ensure only the current owner can call `transferOwnership` and `renounceOwnership`. -- The pending owner must call `acceptOwnership` to finalize the transfer. +- Initialize the `OwnerTwoStepsFacet` with the desired initial owner address during diamond deployment. +- Use the `transferOwnership` function to initiate a change, and ensure the new owner calls `acceptOwnership` to finalize the transfer. +- Store the `OwnerTwoStepsFacet` interface in your diamond contract or a dedicated facets registry for easy access. ## Security Considerations -Access control is critical. The `transferOwnership` function must be restricted to the current owner. The `acceptOwnership` function is intended to be called by the proposed new owner. `renounceOwnership` should also be restricted to the current owner. Incorrect access control could lead to unauthorized ownership changes. No reentrancy concerns as state changes are atomic and do not call external contracts. +Only the current owner can initiate ownership transfers. Any account can accept a pending ownership transfer. Ensure that the address calling `transferOwnership` is indeed the intended owner. Consider the implications of `renounceOwnership` as it makes the contract ownership unrecoverable.
- + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx index e297565b..efe8fa09 100644 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "OwnerTwoStepsMod" description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,9 +25,9 @@ Manages contract ownership with a two-step transfer process. -- Enforces a two-step ownership transfer process for enhanced security. -- Provides explicit functions to check current and pending ownership status. -- Includes a permissionless `renounceOwnership` function to permanently relinquish control. +- Implements a secure two-step ownership transfer via `transferOwnership` and `acceptOwnership`. +- Provides `owner()` and `pendingOwner()` view functions to query current and pending ownership. +- Includes `renounceOwnership()` to permanently disable owner-restricted functions. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module implements a secure two-step ownership transfer mechanism. It prevents accidental ownership loss by requiring a pending owner to explicitly accept the transfer, enhancing contract safety and upgradeability. +This module implements a secure two-step ownership transfer mechanism, ensuring that ownership changes are deliberate and auditable. By requiring explicit acceptance from the new owner, it mitigates risks associated with accidental or malicious ownership transfers, enhancing the overall safety and composability of your diamond. --- @@ -48,7 +48,7 @@ storage-location: erc8042:compose.owner {`struct OwnerStorage { -address owner; + address owner; }`} @@ -59,7 +59,7 @@ storage-location: erc8042:compose.owner.pending {`struct PendingOwnerStorage { -address pendingOwner; + address pendingOwner; }`} @@ -252,34 +252,23 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerTwoSteps} from "./interfaces/IOwnerTwoSteps.sol"; +import {OwnerTwoStepsMod} from "../OwnerTwoStepsMod.sol"; -contract MyDiamondFacet { - IOwnerTwoSteps internal ownerFacet; +contract MyFacet is OwnerTwoStepsMod { + // Assume OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are defined and set + // in the diamond deployment. - constructor(address _ownerFacetAddress) { - ownerFacet = IOwnerTwoSteps(_ownerFacetAddress); + function someOwnerRestrictedFunction() external { + requireOwner(); // Ensure only the owner can call this function + // ... owner-only logic ... } - function handleOwnershipTransfer() external { - address currentOwner = ownerFacet.owner(); - address pending = ownerFacet.pendingOwner(); - - // If there's a pending owner, the current owner can accept it. - // Otherwise, initiate a new transfer. - if (pending != address(0)) { - // Assume the current owner is calling to accept. - ownerFacet.acceptOwnership(); - } else { - // Transfer ownership to a new address. - address newOwner = address(1); // Replace with actual new owner address - ownerFacet.transferOwnership(newOwner); - } + function initiateOwnershipTransfer(address _newOwner) external { + transferOwnership(_newOwner); } - function protectAdminAction() external { - ownerFacet.requireOwner(); - // ... admin-specific logic ... + function acceptNewOwnership() external { + acceptOwnership(); } }`} @@ -287,19 +276,19 @@ contract MyDiamondFacet { ## Best Practices -- Use `transferOwnership` to initiate transfers and `acceptOwnership` to finalize them to prevent accidental ownership changes. -- Implement `requireOwner` checks before critical administrative functions to enforce access control. -- Be aware that `renounceOwnership` permanently removes ownership, disabling all owner-restricted functions. +- Use `requireOwner()` judiciously to protect critical functions, ensuring only the designated owner can execute sensitive operations. +- Handle `OwnerUnauthorizedAccount` and `OwnerAlreadyRenounced` errors appropriately in your calling contracts or frontend applications. +- Be aware that `renounceOwnership()` permanently relinquishes owner privileges; ensure this action is intended and irreversible. ## Integration Notes -The `OwnerTwoStepsMod` utilizes dedicated storage slots for `OwnerStorage` and `PendingOwnerStorage`. Facets can interact with these storage variables via the provided `getOwnerStorage` and `getPendingOwnerStorage` functions, which use inline assembly to access the correct storage locations. Any facet can call these functions to read ownership information. Ownership checks are performed by calling `requireOwner` on the `OwnerTwoStepsMod` facet. +The `OwnerTwoStepsMod` relies on specific storage slots for its `Owner` and `PendingOwner` state variables. These slots are typically defined as constants (e.g., `OWNER_STORAGE_POSITION`, `PENDING_OWNER_STORAGE_POSITION`) within the diamond's facet deployment. Facets interacting with this module can access the owner information via the `owner()` and `pendingOwner()` view functions. The `requireOwner()` internal function enforces access control by checking against the current owner's address.
- + diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx index b41ae979..b396283c 100644 --- a/website/docs/library/diamond/DiamondCutFacet.mdx +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondCutFacet" -description: "Manage diamond facets and functions within a diamond proxy." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondCutFacet.sol" +description: "Manage diamond facets and function registrations." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondCutFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage diamond facets and functions within a diamond proxy. +Manage diamond facets and function registrations. -- Supports adding, replacing, and removing functions and entire facets. -- Allows optional execution of an initialization function during a cut operation. -- Provides access to diamond storage and owner information through dedicated functions. +- Supports adding, replacing, and removing facets and their associated functions. +- Enables atomic upgrades with the `diamondCut` function. +- Provides owner-only control over diamond modifications. ## Overview -The DiamondCutFacet provides essential functionality for managing the diamond proxy's facets and functions. It allows for adding, replacing, and removing functions, as well as executing arbitrary code during the cut operation. This facet is crucial for upgrading and extending the diamond's capabilities. +The DiamondCutFacet provides essential functions for managing the diamond's functionality. It allows for adding, replacing, and removing facets, as well as updating function selectors. This facet is crucial for diamond upgrades and maintaining its modular structure. --- @@ -63,8 +63,8 @@ The DiamondCutFacet provides essential functionality for managing the diamond pr {`struct DiamondStorage { mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; /** - * Array of all function selectors that can be called in the diamond - */ + * Array of all function selectors that can be called in the diamond + */ bytes4[] selectors; }`}
@@ -368,29 +368,27 @@ error InitializationFunctionReverted(address _initializationContractAddress, byt {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {IDiamondCut} from "@compose/diamond/contracts/facets/DiamondCutFacet.sol"; -contract DiamondCutConsumer { - IDiamondCut public diamondCutFacet; +contract Deployer { + address immutable DIAMOND_PROXY; - constructor(address _diamondCutFacetAddress) { - diamondCutFacet = IDiamondCut(_diamondCutFacetAddress); + constructor(address _diamondProxy) { + DIAMOND_PROXY = _diamondProxy; } - function addMyFacet(address _facetAddress, bytes4[] memory _functionSelectors) external { - // Assume DiamondLoupeFacet is already deployed and its selectors are known - // Assume MyFacet is deployed at _facetAddress - diamondCutFacet.diamondCut([(_facetAddress, IDiamondCut.FacetCutAction.Add, _functionSelectors)], address(0), ""); - } + function upgradeDiamond() external { + IDiamondCut diamondCut = IDiamondCut(DIAMOND_PROXY); - function replaceMyFacet(address _newFacetAddress, bytes4[] memory _functionSelectors) external { - // Assume MyFacet is deployed at _newFacetAddress - diamondCutFacet.diamondCut([(_newFacetAddress, IDiamondCut.FacetCutAction.Replace, _functionSelectors)], address(0), ""); - } + // Example: Add a new facet + bytes[] memory facetCuts = new bytes[](1); + // Assume facetCuts[0] contains encoded data for adding a facet + // diamondCut.diamondCut(facetCuts, address(0), "", false); - function removeMyFacet(bytes4[] memory _functionSelectors) external { - // Remove functions associated with a facet - diamondCutFacet.diamondCut([], address(0), ""); // Placeholder for actual function removal logic + // Example: Replace a function + // bytes[] memory replaceCuts = new bytes[](1); + // Assume replaceCuts[0] contains encoded data for replacing a function + // diamondCut.diamondCut(new bytes[](0), address(0), "", false); } }`} @@ -398,19 +396,19 @@ contract DiamondCutConsumer { ## Best Practices -- Use `diamondCut` to atomically add, replace, or remove functions and facets. This ensures consistency and prevents partial upgrades. -- Always provide selectors when adding or replacing facets to ensure the diamond can route calls correctly. -- For upgrades, carefully manage the `InitializationFunctionReverted` error to ensure new facet initializers execute successfully. +- Ensure the caller is authorized before performing any cut operations. +- Carefully manage function selector registrations to avoid conflicts or unintended overwrites. +- Leverage `diamondCut`'s ability to execute an initialization function post-upgrade for proper state setup. ## Security Considerations -Access control is paramount; only authorized addresses (typically the diamond owner) should be able to call `diamondCut` to prevent unauthorized modifications to the diamond's functionality. Ensure that facet addresses provided are valid and that selectors do not conflict with existing functions, especially immutable ones. Reentrancy is mitigated by the atomic nature of the `diamondCut` operation. +This facet is highly sensitive as it modifies the diamond's core functionality. Access control is paramount; only authorized addresses (typically the diamond's owner) should be able to call `diamondCut` and related functions. Incorrectly managing function selectors can lead to unexpected behavior or denial of service. Reentrancy is not an immediate concern for the cut operations themselves, but any initialization function called via `diamondCut` must be reentrancy-guarded.
- + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx index b7de9a9d..db9bb672 100644 --- a/website/docs/library/diamond/DiamondCutMod.mdx +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondCutMod" -description: "Manages facet additions, removals, and replacements on a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondCutMod.sol" +description: "Manages diamond facet additions, replacements, and removals." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondCutMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages facet additions, removals, and replacements on a diamond. +Manages diamond facet additions, replacements, and removals. -- Dynamically adds, replaces, and removes functions from the diamond proxy's routing table. -- Supports batch operations for efficient interface updates. -- Allows for an optional initialization call via `delegatecall` during a cut operation. +- Atomically adds, replaces, or removes multiple functions in a single transaction. +- Supports optional delegatecall execution of an initialization function after the cut. +- Enforces restrictions against modifying immutable functions and prevents redundant operations. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The DiamondCutMod provides essential functions for modifying the diamond's contract interface. It allows for the dynamic addition, replacement, or removal of functions across various facets. This module is crucial for maintaining and evolving the diamond's capabilities post-deployment, enabling upgrades and feature extensions. +The DiamondCutMod provides essential functions for managing the facets of a Compose diamond. It allows for controlled addition, replacement, and removal of functions, ensuring the diamond's logic can be upgraded and extended safely. This module is critical for maintaining the diamond's integrity during upgrades. --- @@ -53,11 +53,11 @@ storage-location: erc8042:compose.diamond {`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; }`} @@ -66,8 +66,8 @@ bytes4[] selectors; {`struct FacetAndPosition { -address facet; -uint32 position; + address facet; + uint32 position; }`} @@ -76,9 +76,9 @@ uint32 position; {`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; }`} @@ -334,47 +334,41 @@ error RemoveFacetAddressMustBeZeroAddress(address _facet); {`pragma solidity ^0.8.30; -import {IDiamondCutMod} from "@compose/diamond-contracts/contracts/modules/diamondCut/IDiamondCutMod.sol"; -import {DiamondCutFacet} from "@compose/diamond-contracts/contracts/modules/diamondCut/DiamondCutFacet.sol"; - -contract MyDiamondConsumerFacet { - IDiamondCutMod constant DIAMOND_CUT_MODULE = IDiamondCutMod(address(this)); // Replace with actual diamond address - - function upgradeDiamond() external { - // Example: Add a new function - address newFacetAddress = address(0x123); // Address of the new facet contract - bytes4[] memory selectorsToAdd = new bytes4[](1); - selectorsToAdd[0] = DiamondCutFacet.addFunctions.selector; // Example selector - - DIAMOND_CUT_MODULE.diamondCut( - address(0), // Facet address for additions (can be zero if only replacing/removing) - selectorsToAdd, - new bytes4[](0), // Selectors to remove - new address[](0), // Facet addresses for replacements (can be empty) - new bytes4[][](0), // New selectors for replacements (can be empty) - address(0), // Target address for initialization call - bytes('') // Initialization calldata - ); +import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/facets/DiamondCutMod.sol"; + +contract MyFacet { + // ... other facet logic ... + + function upgradeDiamond(address _diamondAddress) external { + address[] memory facetAddresses = new address[](1); + facetAddresses[0] = address(this); // Assuming this contract is the new facet + + bytes[] memory functionSigs = new bytes[](1); + functionSigs[0] = bytes4(keccak256(\"myNewFunction(uint256)\")); // Example selector + + IDiamondCut(_diamondAddress).diamondCut(facetAddresses, address(0), functionSigs, address(0), \"\"); } + + // ... other facet logic ... }`} ## Best Practices -- Always ensure the facet address provided for adding functions is valid and contains the intended bytecode. Use `getStorage` to verify facet existence before removal. -- Be aware that `diamondCut` can execute an arbitrary `delegatecall` for initialization, requiring careful validation of the target address and calldata. -- Prefer adding or replacing functions over removing them to maintain backward compatibility where possible. Ensure immutable functions are not targeted for removal or replacement. +- Use `diamondCut` carefully; ensure all function selectors are correctly identified to avoid unintended logic changes or access control bypasses. +- Always verify that the facet addresses provided are deployed and contain the intended bytecode before executing `diamondCut`. +- Handle potential `InitializationFunctionReverted` errors by ensuring any initialization logic within the cut is robust and correctly implemented. ## Integration Notes -This module directly interacts with the diamond's internal storage to manage the mapping of function selectors to facet addresses. Changes made via `diamondCut` are immediately reflected in the diamond proxy's dispatch logic. Facets should use `getStorage` to query the current facet implementations. Facet cuts are atomic; either all operations succeed or none do. The order of operations within a single `diamondCut` call is significant: additions are processed first, then replacements, and finally removals. +The DiamondCutMod directly manipulates the diamond's function selector to facet address mapping. Any changes made via `diamondCut` are immediately reflected in the diamond proxy's routing logic. Facets interact with this module by calling its external functions, typically during upgrade processes. The `DiamondCut` event is emitted upon successful completion of a diamond cut operation, providing an audit trail of changes.
- + diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx index a70aa6fa..c51bd1d8 100644 --- a/website/docs/library/diamond/DiamondInspectFacet.mdx +++ b/website/docs/library/diamond/DiamondInspectFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondInspectFacet" -description: "Inspect diamond storage and facet mappings" -gitSource: "https://github.com/maxnorm/Compose/blob/7e2dd824639f424be644c4ebbb3ca2508167329f/src/diamond/DiamondInspectFacet.sol" +description: "Inspect diamond storage and function-to-facet mappings." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondInspectFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Inspect diamond storage and facet mappings +Inspect diamond storage and function-to-facet mappings. -- Retrieves raw diamond storage bytes directly from its storage slot. -- Maps function selectors to their corresponding facet addresses. -- Enables external inspection of diamond's internal structure. +- Directly retrieves raw diamond storage using inline assembly. +- Maps function selectors to their implementing facet addresses. +- Provides read-only access, ensuring state integrity. ## Overview -The DiamondInspectFacet provides read-only access to the diamond's internal state and function-to-facet mappings. It allows external entities to query the diamond's storage layout and understand which facets handle specific function selectors without needing direct storage access. +The DiamondInspectFacet provides essential read-only capabilities for understanding a Compose diamond's internal state and function distribution. It allows developers to query the diamond's storage layout and map function selectors to their respective facet implementations, aiding in debugging and integration. --- @@ -54,8 +54,8 @@ The DiamondInspectFacet provides read-only access to the diamond's internal stat {`struct DiamondStorage { mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; /** - * Array of all function selectors that can be called in the diamond. - */ + * Array of all function selectors that can be called in the diamond. + */ bytes4[] selectors; }`} @@ -133,25 +133,24 @@ Returns an array of all function selectors and their corresponding facet address {`pragma solidity ^0.8.30; -import {IDiamondInspectFacet} from "@compose/contracts/facets/DiamondInspect/IDiamondInspectFacet.sol"; -import {DiamondProxy} from "@compose/contracts/DiamondProxy.sol"; +import {DiamondInspectFacet} from "@compose/diamond-facet-inspect/DiamondInspectFacet.sol"; +import {IDiamondCut} from "@compose/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; -contract ConsumerFacet { - IDiamondInspectFacet immutable diamondInspectFacet; +contract Consumer { + address immutable diamondAddress; - constructor(address diamondProxyAddress) { - diamondInspectFacet = IDiamondInspectFacet(diamondProxyAddress); + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; } - function inspectDiamond() external view returns (bytes[] memory, address[]) { - // Example: Get all function selectors and their facet addresses - (bytes[] memory selectors, address[] memory facets) = diamondInspectFacet.functionFacetPairs(); - return (selectors, facets); + function inspectStorage() external view returns (bytes memory) { + DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamondAddress); + return inspectFacet.getStorage(); } - function viewStorage() external view returns (bytes memory) { - // Example: Retrieve the diamond's storage struct - return diamondInspectFacet.getStorage(); + function getFunctionFacetPairs() external view returns (IDiamondCut.FacetPair[] memory) { + DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamondAddress); + return inspectFacet.functionFacetPairs(); } }`} @@ -159,19 +158,19 @@ contract ConsumerFacet { ## Best Practices -- Integrate this facet to provide transparency into diamond functionality and state. -- Use `functionFacetPairs` to map function selectors to their respective implementation contracts for auditing and debugging. -- Access `getStorage` cautiously, understanding it returns raw storage bytes and requires careful deserialization based on the diamond's storage layout. +- Integrate this facet into your diamond to enable introspection capabilities. +- Access functions via the diamond proxy address to ensure correct routing. +- Use the retrieved data for debugging, auditing, and dynamic integration. ## Security Considerations -This facet is read-only and does not modify state, mitigating reentrancy risks. However, `getStorage` returns raw bytes; incorrect deserialization could lead to misinterpretation of the diamond's state. Ensure the consumer understands the diamond's storage layout. +This facet is read-only and does not modify state, mitigating reentrancy risks. Access control is managed by the diamond proxy itself. Ensure the diamond's access control mechanisms are robust, as this facet exposes internal mappings.
- + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx index 3215aa34..616a43d5 100644 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondLoupeFacet" -description: "Inspect diamond facets, selectors, and storage." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondLoupeFacet.sol" +description: "Inspect diamond facets, selectors, and storage" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondLoupeFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,17 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Inspect diamond facets, selectors, and storage. +Inspect diamond facets, selectors, and storage -- Provides read-only access to diamond's facet registry. -- Enables querying of facet addresses by function selector. -- Returns all registered facets and their associated function selectors. +- Provides a comprehensive view of diamond's facets and their associated selectors. +- Optimized for gas efficiency when querying large diamonds with many facets and selectors. ## Overview -The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query which facets are registered, the function selectors they implement, and their associated addresses. This facet is crucial for understanding the diamond's internal structure and for building external tools that interact with it. +The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, the function selectors they support, and the addresses of these facets. This facet is crucial for understanding the diamond's composition and for dynamic contract interactions. --- @@ -54,8 +53,8 @@ The DiamondLoupeFacet provides essential introspection capabilities for a Compos {`struct DiamondStorage { mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; /** - * Array of all function selectors that can be called in the diamond. - */ + * Array of all function selectors that can be called in the diamond. + */ bytes4[] selectors; }`} @@ -210,21 +209,23 @@ Gets all facets and their selectors. Returns each unique facet address currently {`pragma solidity ^0.8.30; -import {IDiamondLoupe} from "@compose/diamond-loupe/IDiamondLoupe.sol"; +import {IDiamondLoupe} from "../facets/DiamondLoupeFacet.sol"; -contract DiamondLoupeConsumer { - IDiamondLoupe immutable diamondLoupeFacet; +contract DiamondConsumer { + address immutable diamondAddress; constructor(address _diamondAddress) { - diamondLoupeFacet = IDiamondLoupe(_diamondAddress); + diamondAddress = _diamondAddress; } - function getAllFacets() external view returns (IDiamondLoupe.Facet[] memory) { - return diamondLoupeFacet.facets(); + function getDiamondFacets() public view returns (IDiamondLoupe.Facet[] memory) { + IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); + return loupe.facets(); } - function getFacetAddress(bytes4 _selector) external view returns (address) { - return diamondLoupeFacet.facetAddress(_selector); + function getFacetAddress(bytes4 _selector) public view returns (address) { + IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); + return loupe.facetAddress(_selector); } }`} @@ -232,19 +233,18 @@ contract DiamondLoupeConsumer { ## Best Practices -- Integrate DiamondLoupeFacet into your diamond to enable runtime inspection of its components. -- Use the `facets()` function to retrieve a comprehensive list of registered facets and their selectors for auditing or integration purposes. -- Query `facetAddress(selector)` to determine which facet handles a specific function call, aiding in debugging and understanding execution flow. +- Integrate DiamondLoupeFacet into your diamond to enable introspection for developers and external systems. +- Use the returned data to dynamically route calls or to verify diamond state during upgrades. ## Security Considerations -This facet is read-only and does not modify diamond state, posing no direct security risks. Its primary function is informational. Ensure the diamond's access control mechanisms are correctly implemented in other facets to protect sensitive operations. +This facet is read-only and does not modify state. Its primary security concern is the accuracy of the data it returns, which relies on the correct implementation of diamond proxy logic for tracking facets and selectors.
- + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx index 36b1a7d4..d009a44f 100644 --- a/website/docs/library/diamond/DiamondMod.mdx +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondMod" -description: "Internal functions and storage for diamond proxy." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/DiamondMod.sol" +description: "Manage diamond facets and internal storage." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Internal functions and storage for diamond proxy. +Manage diamond facets and internal storage. -- Manages facet registration and function selector mapping within the diamond proxy. -- Provides the core `diamondFallback` mechanism for dynamic function dispatch to appropriate facets. -- Exposes an internal `getStorage` function for retrieving storage values (though direct use is discouraged in external facets). +- Facet management: Allows adding facets and their function selectors to the diamond. +- Function routing: Provides a fallback mechanism to dispatch calls to the correct facet. +- Internal storage access: Exposes a method to retrieve the diamond's internal storage slot. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The DiamondMod module provides core internal logic for managing diamond facets and handling function calls. It is essential for the diamond proxy's operation, enabling facet registration and dynamic dispatch. +This module provides core functionality for managing facets within a diamond proxy. It enables adding new facets during deployment and offers a fallback mechanism to route external calls to the appropriate facet, ensuring composability and upgradability. --- @@ -53,11 +53,11 @@ storage-location: erc8042:compose.diamond {`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ + bytes4[] selectors; }`} @@ -66,8 +66,8 @@ bytes4[] selectors; {`struct FacetAndPosition { -address facet; -uint32 position; + address facet; + uint32 position; }`} @@ -76,9 +76,9 @@ uint32 position; {`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; }`} @@ -195,21 +195,18 @@ error NoBytecodeAtAddress(address _contractAddress, string _message); {`pragma solidity ^0.8.30; -import {IDiamondMod} from "@compose/contracts/src/diamond/IDiamondMod.sol"; +import {IDiamondMod} from "@compose/diamond-proxy/contracts/modules/DiamondMod.sol"; contract MyFacet { - IDiamondMod internal diamondMod; + IDiamondMod public diamondMod; - constructor(address _diamondMod) { + function initialize(address _diamondMod) external { diamondMod = IDiamondMod(_diamondMod); } - function someFunction() external { - // Example of calling a function that might interact with diamond storage - // Note: Direct calls to diamondMod internal functions are generally not exposed or recommended. - // This is illustrative; actual interaction would be through the diamond proxy itself. - // For instance, a facet might read storage values managed by the diamond proxy. - // bytes32 storageSlot = diamondMod.getStorageSlot(keccak256("myState")); + function getDiamondStorage() external view returns (bytes32) { + // Example of accessing storage via the module + return diamondMod.getStorage(); } }`} @@ -217,19 +214,19 @@ contract MyFacet { ## Best Practices -- Access control for functions like `addFacets` is crucial and should be managed externally, typically by an upgrade agent or owner. -- Ensure that `addFacets` is only called during the initial diamond deployment phase to maintain diamond immutability. -- Handle `FunctionNotFound` errors gracefully in consumer contracts that call functions routed through the diamond proxy. +- Use `addFacets` exclusively during initial diamond deployment to avoid unexpected state changes. +- Implement robust error handling for `diamondFallback` to gracefully manage unknown function selectors. +- Understand that `getStorage` provides access to the diamond's internal storage slot, which is fundamental to the diamond pattern. ## Integration Notes -DiamondMod interacts directly with the diamond's storage layout to store facet information and function mappings. Facets added via `addFacets` are registered in a way that `diamondFallback` can resolve incoming calls. The `getStorage` function allows facets to access state managed by the diamond proxy, adhering to the storage slotting conventions. `addFacets` must be called during deployment; subsequent calls will revert. +This module manages the diamond's internal mapping of function selectors to facet addresses. Facets can interact with this module to retrieve the diamond's storage slot via `getStorage()`. The `addFacets` function is intended for use only during the initial deployment of the diamond contract. Calling it after deployment can lead to the `InvalidActionWhenDeployingDiamond` error.
- + diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx index 847a57f6..041e5ec5 100644 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ExampleDiamond" -description: "Diamond proxy for routing external calls to facets" -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/diamond/example/ExampleDiamond.sol" +description: "Example Diamond for Compose diamond framework" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/example/ExampleDiamond.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Diamond proxy for routing external calls to facets +Example Diamond for Compose diamond framework -- Centralized function routing via delegatecall. -- Supports modularity by allowing dynamic addition/removal of functionality (facets). -- Enables upgradeability by replacing facet implementations without changing the diamond proxy address. +- Demonstrates core diamond proxy structure and initialization. +- Supports facet registration and owner assignment. +- Provides a basic fallback and receive function for Ether handling. ## Overview -The ExampleDiamond acts as a central proxy, managing function routing to various facets. It initializes the diamond by mapping function selectors to their corresponding facet addresses, enabling modular and upgradeable smart contract logic. +The ExampleDiamond contract serves as a foundational template within the Compose diamond framework. It demonstrates core diamond proxy functionalities, including facet registration and owner initialization, providing a starting point for custom diamond deployments. --- @@ -85,51 +85,42 @@ Struct to hold facet address and its function selectors. struct FacetCut { {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose/diamond/contracts/IDiamondCut.sol"; -import {IDiamondLoupe} from "@compose/diamond/contracts/IDiamondLoupe.sol"; +import {IDiamondCut} from "@compose/diamond-cut/src/interfaces/IDiamondCut.sol"; +import {DiamondInit} from "@compose/diamond-init/src/DiamondInit.sol"; -// Assume ExampleDiamond is deployed and initialized -contract Consumer { - IDiamondCut internal immutable diamondCut; - IDiamondLoupe internal immutable diamondLoupe; +contract MyDiamond is IDiamondCut { + address owner; - constructor(address _diamondAddress) { - diamondCut = IDiamondCut(_diamondAddress); - diamondLoupe = IDiamondLoupe(_diamondAddress); + constructor(address _owner, DiamondInit _diamondInit) { + owner = _owner; + // Example of calling DiamondInit to add facets + // _diamondInit.diamondCut(...); // This would typically happen in a separate deployment script } - function addFacet(address _facetAddress, bytes4[] memory _selectors) external { - // Call the diamond's cut function to add a new facet - // Requires owner permissions on the diamond - diamondCut.diamondCut([IDiamondCut.FacetCut({ - facetAddress: _facetAddress, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: _selectors - })], address(0), ""); - } + // Fallback and receive functions would be implemented here or in facets + fallback() external payable {} + receive() external payable {} - function getFacetAddress(bytes4 _selector) external view returns (address) { - return diamondLoupe.facetAddress(_selector); - } + // Other diamond functions or facet functions would be exposed via delegatecall }`} ## Best Practices -- Initialize the diamond with owner and initial facets during deployment. -- Use the `diamondCut` interface for adding, replacing, or removing facets. -- Grant necessary permissions to the entity responsible for managing facet additions/removals. +- Use `DiamondInit` or a similar mechanism for facet installation during deployment rather than directly in the constructor for better upgradeability and clarity. +- Ensure the owner is set correctly during initialization to manage diamond upgrades and facet management. +- Implement `fallback` and `receive` functions explicitly or ensure they are handled by registered facets to manage arbitrary calls and Ether reception. ## Security Considerations -Ensure that only authorized addresses can call `diamondCut` to prevent unauthorized facet manipulation. Input validation for facet addresses and selectors is crucial. Be mindful of state coupling between facets that might share storage slots. +The constructor directly sets the owner. Ensure this initialization is performed securely and by a trusted entity. The `fallback` and `receive` functions, if not implemented in facets, will allow any call or Ether transfer to the diamond, which may be undesirable if not properly guarded by access control mechanisms in other facets.
- + diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx index ad56c9b3..df0c9aa9 100644 --- a/website/docs/library/diamond/example/index.mdx +++ b/website/docs/library/diamond/example/index.mdx @@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx index afceb81f..7919e942 100644 --- a/website/docs/library/diamond/index.mdx +++ b/website/docs/library/diamond/index.mdx @@ -21,35 +21,35 @@ import Icon from '@site/src/components/ui/Icon'; /> } size="medium" /> } size="medium" /> } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx index ad461096..1afb8426 100644 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC165Mod" -description: "Implement ERC-165 interface detection." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/interfaceDetection/ERC165/ERC165Mod.sol" +description: "Implement ERC-165 standard interface detection." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/interfaceDetection/ERC165/ERC165Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Implement ERC-165 interface detection. +Implement ERC-165 standard interface detection. - Implements the ERC-165 standard for interface detection. -- Provides a dedicated storage slot for interface support data. -- Requires explicit registration of supported interfaces. +- Provides a dedicated storage slot for ERC-165 interface flags. +- Facilitates interoperability by allowing facets to declare supported standards. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC165Mod provides the necessary storage and internal functions to implement the ERC-165 standard for interface detection. This allows other contracts to query which interfaces a diamond proxy supports, enhancing composability and interoperability. +This module provides the necessary internal functions and storage layout to implement the ERC-165 standard for interface detection within a Compose diamond. By registering supported interfaces, facets can signal their capabilities to external callers, ensuring interoperability and adherence to standards. --- @@ -46,10 +46,10 @@ The ERC165Mod provides the necessary storage and internal functions to implement {`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; + /* + * @notice Mapping of interface IDs to whether they are supported + */ + mapping(bytes4 => bool) supportedInterfaces; }`} @@ -116,30 +116,16 @@ Register that a contract supports an interface Call this function during initial {`pragma solidity ^0.8.30; -import {ERC165Mod} from "@compose/modules/erc165/ERC165Mod.sol"; - -contract MyFacet { - ERC165Mod.Storage private _erc165Storage; +import {LibERC165} from "@compose-protocol/diamond-contracts/contracts/modules/ERC165/LibERC165.sol"; +import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; +contract MyERC721Facet { /** - * @notice Initializes the ERC165 module and registers the ERC721 interface. + * @notice Initializes the ERC721 facet, registering its supported interfaces. */ function initialize() external { - // Bind storage to the correct slot - ERC165Mod.bindStorage(address(this), _erc165Storage); - - // Register the interface ID for the IERC721 interface - ERC165Mod.registerInterface(_erc165Storage, type(IERC721).interfaceId); - } - - /** - * @notice Queries if the diamond supports a given interface ID. - * @param _interfaceId The interface ID to query. - * @return bool True if the interface is supported, false otherwise. - */ - function supportsInterface(bytes4 _interfaceId) external view returns (bool) { - // Delegate to the ERC165 module's internal function - return ERC165Mod.supportsInterface(_erc165Storage, _interfaceId); + // Register ERC721 interface support + LibERC165.registerInterface(type(IERC721).interfaceId); } }`} @@ -147,19 +133,19 @@ contract MyFacet { ## Best Practices -- Call `ERC165Mod.bindStorage` during facet initialization to correctly map the module's storage. -- Register all supported interface IDs during initialization using `ERC165Mod.registerInterface`. -- Implement `supportsInterface` in your facet to delegate to `ERC165Mod.supportsInterface`. +- Call `registerInterface` during facet initialization to declare supported interfaces. +- Ensure the `ERC165Mod` is correctly integrated into the diamond's storage layout. +- Rely on the diamond's access control for calling `registerInterface` if necessary, though typically done during initialization. ## Integration Notes -The ERC165Mod utilizes its own storage slot to maintain a mapping of supported interface IDs. Facets must explicitly bind to this storage using `ERC165Mod.bindStorage` during their initialization phase. The `supportsInterface` function within the ERC165 module checks this internal mapping. Any facet that needs to declare support for an interface must call `ERC165Mod.registerInterface` during its initialization, ensuring that the interface ID is added to the module's registry before the diamond is made operational. +The `ERC165Mod` utilizes a fixed storage slot for its `ERC165Storage` struct. Facets that implement ERC-165 functionality must call `LibERC165.registerInterface` during their initialization phase. This function updates the internal mapping within the `ERC165Storage` struct, making the supported interfaces discoverable via the standard ERC-165 `supportsInterface` function implemented at the diamond proxy level.
- + diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx index 26ecf715..6ddd1b81 100644 --- a/website/docs/library/interfaceDetection/ERC165/index.mdx +++ b/website/docs/library/interfaceDetection/ERC165/index.mdx @@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx index 04b22f0c..d2e942d2 100644 --- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC1155Facet" -description: "Manage ERC-1155 fungible and non-fungible tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC1155/ERC1155Facet.sol" +description: "Manages fungible and non-fungible tokens via ERC-1155 standard." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC1155/ERC1155Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage ERC-1155 fungible and non-fungible tokens. +Manages fungible and non-fungible tokens via ERC-1155 standard. -- Supports both fungible and non-fungible ERC-1155 tokens. -- Provides standard `balanceOf`, `balanceOfBatch`, `safeTransferFrom`, and `safeBatchTransferFrom` functions. -- Includes `setApprovalForAll` and `isApprovedForAll` for operator permissions. -- Allows for dynamic token URIs using a base URI and individual token URI overrides. +- Supports ERC-1155 multi-token standard. +- Provides batch transfer and balance checking capabilities. +- Manages token approvals for operators. ## Overview -The ERC1155Facet enables a Compose diamond to manage ERC-1155 compliant tokens. It provides standard functions for token transfers, batch transfers, balance checks, and operator approvals, facilitating the creation and management of multi-token standards within a single diamond. +The ERC1155Facet implements the ERC-1155 multi-token standard, enabling a diamond to manage both fungible and non-fungible tokens. It provides core functionalities for balance tracking, approvals, and transfers, facilitating a unified token management system within the diamond. --- @@ -624,50 +623,45 @@ error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); {`pragma solidity ^0.8.30; -import {IERC1155Facet} from "@compose-protocol/diamond-contracts/contracts/facets/erc1155/IERC1155Facet.sol"; -import {ERC1155Facet} from "@compose-protocol/diamond-contracts/contracts/facets/erc1155/ERC1155Facet.sol"; +import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; -contract MyDiamond is IERC1155Facet { - // ... other facets and diamond setup ... +contract ERC1155Consumer { + IERC1155Facet private immutable _erc1155Facet; - function supportsInterface(bytes4 _interfaceId) external view virtual override returns (bool) { - // ... other interface checks ... - if (_interfaceId == type(IERC1155Facet).interfaceId) { - return true; - } - return false; + constructor(address _diamondAddress) { + _erc1155Facet = IERC1155Facet(_diamondAddress); } - // Example: Transferring tokens - function transferERC1155(address _to, uint256 _id, uint256 _value) external { - // Assume caller is approved or owner - safeTransferFrom(_to, msg.sender, _id, _value, ""); + function getTokenBalance(address _account, uint256 _id) external view returns (uint256) { + return _erc1155Facet.balanceOf(_account, _id); } - // Example: Checking balance - function getERC1155Balance(address _account, uint256 _id) external view returns (uint256) { - return balanceOf(_account, _id); + function getTokenUri(uint256 _id) external view returns (string memory) { + return _erc1155Facet.uri(_id); } -} -`} + + function transferSingleToken(address _from, address _to, uint256 _id, uint256 _value) external { + _erc1155Facet.safeTransferFrom(_from, _to, _id, _value, ""); + } +}`} ## Best Practices -- Initialize the `ERC1155Facet` with `baseURI` and `tokenURIs` as needed during diamond deployment. -- Ensure appropriate access control is implemented on functions that modify token balances or approvals, if required by your diamond's logic. -- Store the `ERC1155Facet` contract address in the diamond's facet registry. +- Initialize the ERC1155Facet with the diamond proxy address before interaction. +- Ensure necessary approvals are set using `setApprovalForAll` before performing transfers on behalf of another account. +- Handle token URIs carefully, considering both base URI concatenation and the `{id}` placeholder. ## Security Considerations -The `ERC1155Facet` itself does not enforce ownership or complex access controls beyond what is standard for ERC-1155. Ensure that the diamond's upgradeability mechanism and any custom logic interacting with this facet correctly manage permissions and prevent unauthorized token transfers or approvals. Input validation for token IDs and amounts is handled by the facet to prevent basic errors. +Ensure sufficient balance before initiating transfers to prevent `ERC1155InsufficientBalance` errors. Validate `_from`, `_to`, and `_id` parameters to prevent unintended state changes or errors. Approvals should be managed carefully to prevent unauthorized token movements. The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks to ensure the recipient contract implements the `onERC1155Received` hook if applicable.
- + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx index 16268088..bf424900 100644 --- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC1155Mod" -description: "Manages ERC-1155 token balances, transfers, and metadata." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC1155/ERC1155Mod.sol" +description: "Manage ERC-1155 tokens with minting, burning, and transfers." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC1155/ERC1155Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-1155 token balances, transfers, and metadata. +Manage ERC-1155 tokens with minting, burning, and transfers. -- Supports minting and burning of single and batch token types. -- Implements safe transfer logic compliant with EIP-1155. -- Allows setting base and token-specific URIs for metadata. +- Full ERC-1155 token lifecycle management (minting, burning). +- Supports atomic batch operations for efficiency. +- Implements EIP-1155 safe transfer logic with receiver validation. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module implements the core ERC-1155 token functionality, including minting, burning, and safe transfers. It ensures proper balance tracking and adherence to the ERC-1155 standard, enabling composable multi-token systems within a diamond. +The ERC1155Mod provides a comprehensive interface for managing ERC-1155 tokens within a Compose diamond. It enables minting, burning, and safe transfers of both single and batch token types, adhering to EIP-1155 standards. This module is crucial for any diamond that needs to support fungible or semi-fungible token functionalities. --- @@ -48,11 +48,11 @@ ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc {`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; }`} @@ -561,24 +561,21 @@ error ERC1155MissingApprovalForAll(address _operator, address _owner); {`pragma solidity ^0.8.30; -import {IERC1155Mod} from "@compose/core/src/modules/ERC1155Mod.sol"; +import {IERC1155Mod} from "@compose/modules/ERC1155Mod.sol"; contract MyFacet { - IERC1155Mod public immutable erc1155Mod; + address immutable diamondProxy; - constructor(address diamondAddress) { - // Assuming ERC1155Mod facet is already deployed and accessible - erc1155Mod = IERC1155Mod(diamondAddress); + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; } - function mintErc1155Tokens(address to, uint256 id, uint256 amount) external { - // Call the mint function from the ERC1155Mod facet - erc1155Mod.mint(to, id, amount); + function mintErc1155Tokens(address _to, uint256 _id, uint256 _amount) external { + IERC1155Mod(diamondProxy).mint(_to, _id, _amount); } - function transferErc1155Tokens(address from, address to, uint256 id, uint256 amount) external { - // Call the safeTransferFrom function from the ERC1155Mod facet - erc1155Mod.safeTransferFrom(from, to, id, amount, ""); + function transferErc1155Tokens(address _from, address _to, uint256 _id, uint256 _amount) external { + IERC1155Mod(diamondProxy).safeTransferFrom(_from, _to, _id, _amount, ""); } }`} @@ -586,19 +583,19 @@ contract MyFacet { ## Best Practices -- Always validate receiver addresses when minting or transferring to contracts to ensure they implement the ERC1155Receiver interface. -- Use `safeTransferFrom` and `safeBatchTransferFrom` for transfers involving contract recipients to ensure proper handling of tokens. -- Handle potential `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, `ERC1155InvalidReceiver`, `ERC1155InvalidSender`, and `ERC1155MissingApprovalForAll` errors gracefully. +- Always validate token IDs and amounts before minting or burning to prevent unexpected state changes. +- Ensure that approvals are managed correctly before performing transfers to prevent unauthorized token movements. +- Handle `ERC1155InsufficientBalance` and `ERC1155InvalidArrayLength` errors to gracefully manage transaction failures. ## Integration Notes -This module interacts with the diamond's storage to manage ERC-1155 balances and token URIs. The storage is accessed via a predefined slot. Facets interacting with this module should be aware of the ERC-1155 storage layout and potential state changes. +The ERC1155Mod utilizes a dedicated storage slot within the diamond's main storage contract to manage token balances, approvals, and URIs. Facets interacting with this module will need to call its functions through the diamond proxy. The `getStorage` function can be used to directly access the module's internal storage struct, which contains mappings for balances (`_balances`), operator approvals (`_operatorApprovals`), and token URIs (`_tokenURIs`), along with the `_baseURI`.
- + diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx index 2ce0d206..f43c0cd3 100644 --- a/website/docs/library/token/ERC1155/index.mdx +++ b/website/docs/library/token/ERC1155/index.mdx @@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx index 91a21928..8f1d40cd 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BurnFacet" -description: "Burn ERC20 tokens from caller or spender allowance." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +description: "Burn ERC20 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20/ERC20BurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,17 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC20 tokens from caller or spender allowance. +Burn ERC20 tokens within a Compose diamond. -- Supports burning tokens from the caller's balance. -- Enables burning tokens from another account's balance using allowances. +- Allows burning of ERC20 tokens from the caller's balance. +- Supports burning ERC20 tokens from another account using allowances. +- Emits `Transfer` events to the zero address upon successful burns, conforming to ERC20 standards. +- Integrates with the Compose diamond storage pattern. ## Overview -The ERC20BurnFacet allows for the destruction of ERC20 tokens within a Compose diamond. It provides functions to burn tokens directly from the caller's balance or by deducting from an allowance granted by another account. This facet facilitates token supply reduction mechanisms. +The ERC20BurnFacet provides the functionality to burn ERC20 tokens directly within a Compose diamond. It allows users to decrease their own token balances or burn tokens on behalf of others, provided they have the necessary allowances. This facet integrates with the diamond's storage pattern to manage ERC20 state. --- @@ -207,21 +209,26 @@ error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _ {`pragma solidity ^0.8.30; -import {IERC20BurnFacet} from "@compose/diamond/facets/ERC20BurnFacet.sol"; +import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; +import {DiamondABI} from "@compose/contracts/DiamondABI.sol"; -contract BurnExample { - address immutable diamondAddress; +contract ERC20BurnConsumer { + address immutable DIAMOND_ADDRESS; constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; + DIAMOND_ADDRESS = _diamondAddress; } - function burnTokens(uint256 _amount) public { - IERC20BurnFacet(diamondAddress).burn(_amount); + function burnMyTokens(address _tokenAddress, uint256 _amount) external { + bytes4 selector = IERC20BurnFacet.burn.selector; + (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _tokenAddress, _amount)); + require(success, "Burn failed"); } - function burnTokensFromSpender(address _from, uint256 _amount) public { - IERC20BurnFacet(diamondAddress).burnFrom(_from, _amount); + function burnTokensFromOthers(address _tokenAddress, address _from, uint256 _amount) external { + bytes4 selector = IERC20BurnFacet.burnFrom.selector; + (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _tokenAddress, _from, _amount)); + require(success, "Burn from failed"); } }`} @@ -229,18 +236,19 @@ contract BurnExample { ## Best Practices -- Ensure the ERC20 token contract is correctly deployed and accessible via the diamond proxy. -- Use `burnFrom` only after verifying sufficient allowance has been set for the spender. +- Ensure the ERC20BurnFacet is correctly added to your diamond and its functions are properly routed. +- Manage token allowances carefully before calling `burnFrom` to prevent unintended token loss. +- The `getStorage` function can be used to inspect ERC20 storage directly if needed, but direct manipulation is not recommended. ## Security Considerations -The `burn` and `burnFrom` functions reduce the total supply of ERC20 tokens. Ensure that the logic triggering these burns aligns with the intended tokenomics. `burnFrom` requires that the caller has been granted sufficient allowance by the `_from` address prior to invocation. The facet relies on the underlying ERC20 token implementation for balance and allowance checks, and emits `Transfer` events to the zero address as per ERC20 standards for burning. +This facet relies on the underlying ERC20 token's transfer logic and allowance mechanism. Ensure the token contract itself is secure. The `burnFrom` function requires the caller to have sufficient allowance, and the `burn` function requires the caller to have sufficient balance. Incorrect allowances or balances will revert with `ERC20InsufficientAllowance` or `ERC20InsufficientBalance` respectively. Reentrancy is not a concern as these are state-mutating calls that do not yield control back to external contracts before state changes are finalized.
- + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx index 12395769..77445dbe 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20Facet" -description: "Implements the ERC20 token standard for Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20/ERC20Facet.sol" +description: "Standard ERC-20 token functionality for Compose diamonds." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20/ERC20Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Implements the ERC20 token standard for Compose diamonds. +Standard ERC-20 token functionality for Compose diamonds. -- Full ERC20 standard compliance. -- Manages token state via the diamond's storage pattern. -- Supports standard token operations: name, symbol, decimals, totalSupply, balanceOf, allowance, approve, transfer, transferFrom. +- Full ERC-20 standard compliance. +- Standardized interface for token operations within a diamond. +- Read-only functions for token metadata and balances. +- State-changing functions for transfers and approvals. ## Overview -The ERC20Facet provides standard ERC20 token functionality, including balance tracking, transfers, and allowances. It integrates with the diamond's storage pattern to manage token state and adheres to the ERC20 specification for broad compatibility. +The ERC20Facet provides a complete implementation of the ERC-20 token standard, enabling tokens to be managed within a Compose diamond. It exposes standard read functions and state-changing operations like transfers and approvals, ensuring interoperability and adherence to the widely adopted token standard. --- @@ -522,29 +523,24 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Facet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20/IERC20Facet.sol"; -import {DiamondProxy} from "@compose-protocol/diamond-contracts/contracts/DiamondProxy.sol"; +import {IERC20Facet} from "@compose-protocol/diamond/facets/ERC20/IERC20Facet.sol"; -contract ERC20User { +contract ERC20Consumer { IERC20Facet public erc20Facet; - constructor(address _diamondProxyAddress) { - erc20Facet = IERC20Facet(_diamondProxyAddress); + constructor(address _diamondAddress) { + erc20Facet = IERC20Facet(_diamondAddress); } - function getTokenName() public view returns (string memory) { + function getTokenName() external view returns (string memory) { return erc20Facet.name(); } - function getTokenSymbol() public view returns (string memory) { - return erc20Facet.symbol(); + function transferTokens(address _to, uint256 _amount) external { + erc20Facet.transfer(_to, _amount); } - function getTokenBalance(address _account) public view returns (uint256) { - return erc20Facet.balanceOf(_account); - } - - function approveSpend(address _spender, uint256 _amount) public { + function approveSpender(address _spender, uint256 _amount) external { erc20Facet.approve(_spender, _amount); } }`} @@ -553,19 +549,19 @@ contract ERC20User { ## Best Practices -- Initialize the ERC20Facet with appropriate token name, symbol, and decimals during diamond deployment. -- Ensure the `approve` function is used before calling `transferFrom` to prevent unexpected token movements. -- Manage access control for administrative functions (if any) outside this facet, as standard ERC20 operations are permissionless. +- Initialize the ERC20Facet with the correct diamond storage slot during diamond deployment. +- Ensure the `approve` function is used before `transferFrom` to manage token allowances securely. +- Access token metadata (name, symbol, decimals) via the `external view` functions for off-chain or read-only operations. ## Security Considerations -Standard ERC20 vulnerabilities apply. Ensure sufficient allowances are set before using `transferFrom`. Reentrancy is mitigated by the diamond proxy's architecture. Input validation is handled within the facet functions to prevent invalid operations. +This facet implements standard ERC-20 logic. Ensure proper access control is configured at the diamond level for functions that modify token balances or allowances, especially `transfer` and `approve`. Input validation for addresses and amounts is handled internally by the facet's error conditions. Reentrancy is mitigated by the diamond proxy's reentrancy guard, if implemented.
- + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx index 88421578..20304e5c 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20Mod" -description: "ERC-20 token logic with standard functions and storage." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20/ERC20Mod.sol" +description: "ERC-20 token standard implementation." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20/ERC20Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -ERC-20 token logic with standard functions and storage. +ERC-20 token standard implementation. -- Implements standard ERC-20 `transfer`, `transferFrom`, `approve`, `mint`, and `burn` operations. -- Provides a deterministic storage layout via `getStorage`, compatible with diamond storage patterns. -- Defines custom errors for common ERC-20 failure conditions, improving gas efficiency and clarity. +- Implements standard ERC-20 functions: `transfer`, `approve`, `transferFrom`. +- Supports token minting and burning operations. +- Provides access to ERC-20 specific storage via `getStorage`. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC20Mod provides essential ERC-20 token functionality, including minting, burning, transfers, and approvals. It defines a standard storage layout for ERC-20 state, enabling modular integration into Compose diamonds. This allows diamonds to manage ERC-20 tokens efficiently and composably. +This module provides the core logic and storage for the ERC-20 token standard. It enables standard token operations like transfers, approvals, minting, and burning, making it a fundamental building block for any fungible token within a Compose diamond. --- @@ -46,12 +46,12 @@ The ERC20Mod provides essential ERC-20 token functionality, including minting, b {`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; }`} @@ -378,28 +378,30 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Mod } from "./interfaces/IERC20Mod.sol"; -import { ERC20Mod } from "./ERC20Mod.sol"; +import {IERC20Mod } from "./IERC20Mod.sol"; +import { ERC20Storage } from "./ERC20Storage.sol"; -contract MyERC20Facet { - // This facet would be part of a diamond proxy. - // The diamond's storage layout defines the ERC20Mod's storage slot. +contract ERC20Facet { + ERC20Storage private immutable _storage; - function transferTokens(address to, uint256 amount) external { - // Assume diamond storage is correctly initialized for ERC20Mod. - // Call the internal function via the diamond proxy mechanism. - IERC20Mod(address(this)).transfer(to, amount); + constructor(address _diamondAddress) { + // Bind storage to the diamond's storage slot + _storage = ERC20Storage(_diamondAddress); } - function approveSpending(address spender, uint256 amount) external { - IERC20Mod(address(this)).approve(spender, amount); + function transfer(address _to, uint256 _value) external returns (bool) { + // Call the internal transfer function from the module + return IERC20Mod(_storage).transfer(_to, _value); } - function mintTokens(address to, uint256 amount) external { - // This would likely require specific access control mechanisms - // managed by the diamond's access control facet. - // For demonstration, assuming it's callable. - IERC20Mod(address(this)).mint(to, amount); + function approve(address _spender, uint256 _value) external returns (bool) { + // Call the internal approve function from the module + return IERC20Mod(_storage).approve(_spender, _value); + } + + function transferFrom(address _from, address _to, uint256 _value) external returns (bool) { + // Call the internal transferFrom function from the module + return IERC20Mod(_storage).transferFrom(_from, _to, _value); } }`} @@ -407,19 +409,19 @@ contract MyERC20Facet { ## Best Practices -- Ensure the ERC20Mod's storage slot is correctly initialized before deploying facets that interact with it. -- Implement robust access control within your diamond for sensitive functions like `mint` and `burn`. -- Handle custom errors like `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` gracefully in your facets. +- Use `transfer` and `transferFrom` for token movements, ensuring sufficient balances and allowances are checked. +- Implement `approve` carefully to manage token delegation, and be aware of potential reentrancy risks if interacting with external contracts in `transferFrom`. +- Utilize custom errors for clear revert reasons, such as `ERC20InsufficientBalance` or `ERC20InsufficientAllowance`. ## Integration Notes -The ERC20Mod relies on the diamond's storage pattern. The `getStorage` function uses inline assembly to bind to a fixed storage slot. Facets interacting with ERC20Mod will call its functions through the diamond proxy. The storage layout for ERC20Mod is defined within the module itself and should not be altered by other facets to maintain compatibility. Any changes to the ERC20Mod storage struct must be carefully managed during diamond upgrades to preserve state. +The ERC20Mod module utilizes a fixed storage slot for its `ERC20Storage` struct. Facets interacting with this module must bind to this storage slot using `ERC20Storage(_diamondAddress)` and call the module's functions via the `IERC20Mod` interface. The module maintains invariants for token balances, allowances, and total supply, which are updated atomically during its operations.
- + diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx index 00ba6f43..fe7c1ddd 100644 --- a/website/docs/library/token/ERC20/ERC20/index.mdx +++ b/website/docs/library/token/ERC20/ERC20/index.mdx @@ -14,21 +14,21 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx index 81a6348c..2689eeab 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain ERC20 token bridging operations." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +description: "Facilitates cross-chain token transfers for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Facilitates cross-chain ERC20 token bridging operations. +Facilitates cross-chain token transfers for ERC-20 tokens. -- Role-based access control for cross-chain operations, requiring the `trusted-bridge` role. -- Explicit functions for cross-chain minting and burning of ERC20 tokens. -- Utilizes inline assembly for efficient retrieval of storage structs. +- Enables cross-chain minting and burning of ERC-20 tokens. +- Enforces authorization for bridge operations via the `trusted-bridge` role. +- Provides internal checks for token bridging trust. ## Overview -The ERC20BridgeableFacet enables secure and controlled cross-chain transfers of ERC20 tokens within a Compose diamond. It provides functions for minting and burning tokens on behalf of trusted bridge operators, ensuring integrity and proper role-based access. +The ERC20BridgeableFacet enables secure and controlled cross-chain minting and burning of ERC-20 tokens. It relies on a trusted bridge role for authorization, ensuring only authorized entities can initiate these operations. This facet provides essential functions for bridging assets across different blockchain networks. --- @@ -372,33 +372,25 @@ error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _ {`pragma solidity ^0.8.30; -import {IERC20BridgeableFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; -import {Diamond} from "@compose-protocol/diamond-contracts/contracts/Diamond.sol"; +import {IERC20BridgeableFacet} from "@compose/diamond/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; contract Deployer { - function deploy() external { - // Assume diamond is already deployed and initialized - Diamond diamond = Diamond(address(0x123)); + address immutable diamondAddress; + IERC20BridgeableFacet immutable erc20BridgeableFacet; - // Get the ERC20BridgeableFacet interface - IERC20BridgeableFacet erc20BridgeableFacet = IERC20BridgeableFacet(address(diamond)); - - // Example: Mint tokens across chains (called by a trusted bridge operator) - address tokenAddress = address(0xabc); - address recipient = address(0xdef); - uint256 amount = 100 ether; - bytes32 metadata = "0x"; - - // Assuming the caller has the 'trusted-bridge' role - erc20BridgeableFacet.crosschainMint(tokenAddress, recipient, amount, metadata); + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + erc20BridgeableFacet = IERC20BridgeableFacet(diamondAddress); + } - // Example: Burn tokens across chains (called by a trusted bridge operator) - address sender = address(0xghi); - uint256 burnAmount = 50 ether; - bytes32 burnMetadata = "0x"; + function mintCrossChain(address _token, address _to, uint256 _amount) external { + // Assumes the caller has the 'trusted-bridge' role. + erc20BridgeableFacet.crosschainMint(_token, _to, _amount); + } - // Assuming the caller has the 'trusted-bridge' role - erc20BridgeableFacet.crosschainBurn(tokenAddress, sender, burnAmount, burnMetadata); + function burnCrossChain(address _token, address _from, uint256 _amount) external { + // Assumes the caller has the 'trusted-bridge' role. + erc20BridgeableFacet.crosschainBurn(_token, _from, _amount); } }`} @@ -406,19 +398,18 @@ contract Deployer { ## Best Practices -- Ensure the `trusted-bridge` role is correctly assigned to authorized bridge operator addresses via the Access Control facet. -- Store the ERC20 token addresses that will be bridged in a secure and auditable manner, likely managed by a separate facet or contract. -- Verify that the diamond proxy has the ERC20BridgeableFacet correctly appended and selectors are mapped. +- Ensure the 'trusted-bridge' role is correctly assigned to authorized bridge contracts or addresses. +- Use `getERC20Storage` and `getAccessControlStorage` to safely access facet storage when implementing custom logic or extensions. ## Security Considerations -This facet is callable by addresses with the `trusted-bridge` role. Ensure this role is granted with extreme caution and only to verified, audited bridge operator contracts or addresses. Input validation for token addresses, amounts, and recipient/sender addresses is critical to prevent unexpected state changes or token loss. Reentrancy is not directly applicable as functions do not make external calls that return control to the caller within the same execution context, but external calls to the bridged ERC20 token contract should be considered in the overall security model. +Cross-chain operations are sensitive. Ensure the `trusted-bridge` role is strictly managed. The `crosschainMint` and `crosschainBurn` functions are only callable by addresses holding the `trusted-bridge` role, preventing unauthorized token supply manipulation. Input validation is performed by `checkTokenBridge`.
- + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx index e4a1e49b..4fbced14 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BridgeableMod" -description: "Manages cross-chain ERC20 token transfers and minting." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +description: "Manage cross-chain ERC20 token operations and bridge access." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages cross-chain ERC20 token transfers and minting. +Manage cross-chain ERC20 token operations and bridge access. - Enables secure cross-chain minting and burning of ERC20 tokens. -- Enforces access control, restricting operations to addresses with the `trusted-bridge` role. -- Provides helper functions to access internal storage for auditing and debugging. +- Enforces access control for bridge operations, requiring the `trusted-bridge` role. +- Integrates with Compose's diamond storage pattern for state management. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC20Bridgeable module enables secure cross-chain operations for ERC20 tokens. It provides functions for minting and burning tokens on behalf of other chains, with strict access control to ensure only trusted bridge operators can perform these actions. This module is crucial for maintaining the integrity of token balances across distributed ledger environments. +The ERC20BridgeableMod provides functionality for cross-chain token minting and burning, ensuring secure interactions with trusted bridge operators. It leverages Compose's storage pattern to manage ERC20 and AccessControl state within the diamond. --- @@ -46,7 +46,7 @@ The ERC20Bridgeable module enables secure cross-chain operations for ERC20 token {`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; }`} @@ -57,8 +57,8 @@ ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc804 {`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; }`} @@ -380,19 +380,20 @@ error ERC20InvalidSender(address _sender); {`pragma solidity ^0.8.30; -import {IERC20BridgeableFacet} from "@compose/contracts/src/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; +import {IERC20BridgeableMod} from "./interfaces/IERC20BridgeableMod.sol"; +import {DiamondStorage} from "./DiamondStorage.sol"; contract MyFacet { - IERC20BridgeableFacet internal constant ERC20_BRIDGEABLE = IERC20BridgeableFacet(address(this)); + using DiamondStorage for DiamondStorage; - function exampleCrosschainBurn(address _token, address _to, uint256 _amount) external { - // Ensure the caller has the 'trusted-bridge' role before calling - ERC20_BRIDGEABLE.crosschainBurn(_token, _to, _amount); + function exampleCrosschainMint(address _token, address _to, uint256 _amount) external { + IERC20BridgeableMod bridgeableFacet = IERC20BridgeableMod(address(this)); + bridgeableFacet.crosschainMint(_token, _to, _amount); } - function exampleCrosschainMint(address _token, address _to, uint256 _amount) external { - // Ensure the caller has the 'trusted-bridge' role before calling - ERC20_BRIDGEABLE.crosschainMint(_token, _to, _amount); + function exampleCrosschainBurn(address _token, address _from, uint256 _amount) external { + IERC20BridgeableMod bridgeableFacet = IERC20BridgeableMod(address(this)); + bridgeableFacet.crosschainBurn(_token, _from, _amount); } }`} @@ -400,19 +401,19 @@ contract MyFacet { ## Best Practices -- Ensure the `trusted-bridge` role is correctly configured in the AccessControl facet before using cross-chain functions. -- Handle potential `ERC20InsufficientBalance`, `ERC20InvalidReciever`, or `ERC20InvalidSender` errors gracefully. -- Verify that the token address provided to `crosschainBurn` and `crosschainMint` is a valid ERC20 token contract. +- Ensure the `trusted-bridge` role in AccessControl is granted only to verified bridge operators to prevent unauthorized minting/burning. +- Handle `ERC20InsufficientBalance`, `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, `ERC20InvalidReciever`, and `ERC20InvalidSender` errors to gracefully manage cross-chain operation failures. +- When upgrading facets, ensure compatibility with existing ERC20 and AccessControl storage layouts to maintain data integrity. ## Integration Notes -This module interacts with the diamond's storage using predefined slots for ERC20 and AccessControl data. The `getERC20Storage` and `getAccessControlStorage` functions provide access to these structs. The `checkTokenBridge` internal function relies on the `trusted-bridge` role managed by the AccessControl facet. Any changes to the diamond's storage layout that affect these slots may break this module's functionality. +This module utilizes Compose's storage pattern. `getERC20Storage` and `getAccessControlStorage` are provided as helpers to access their respective storage structs from predefined diamond storage slots. Functions like `crosschainMint` and `crosschainBurn` interact with these storage areas to validate bridge access and update token balances. Ensure that the AccessControl facet is correctly initialized with the `trusted-bridge` role before deploying or upgrading the ERC20BridgeableMod facet.
- + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx index fdb26a6c..06d24e29 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx @@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx index 61edbbcf..13c14f64 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20PermitFacet" -description: "Manages ERC-20 token approvals with EIP-2612 permit functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +description: "Enables EIP-2612 permit functionality for ERC-20 tokens." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-20 token approvals with EIP-2612 permit functionality. +Enables EIP-2612 permit functionality for ERC-20 tokens. - Implements EIP-2612 permit functionality for ERC-20 tokens. -- Provides `nonces`, `DOMAIN_SEPARATOR`, and `permit` functions for signature verification and allowance setting. -- Enhances gas efficiency by allowing off-chain signature generation for approvals. +- Allows token owners to grant allowances via signed messages. +- Reduces transaction overhead by enabling off-chain signature generation. +- Provides functions to retrieve nonces and the domain separator for signature construction. ## Overview -The ERC20PermitFacet enables EIP-2612 compliant token approvals, allowing users to grant allowances to spenders via signed messages. This enhances gas efficiency by enabling off-chain signing of approvals, reducing the need for direct on-chain transactions for this purpose. +The ERC20PermitFacet integrates EIP-2612's permit functionality into a Compose diamond. It allows token owners to grant allowances to spenders via a signed message, removing the need for an explicit `approve` transaction. This enhances user experience by reducing transaction costs and improving efficiency. --- @@ -286,21 +287,41 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; -import {DiamondLoupeFacet} from "@diamond-labs/diamond-runtime/facets/DiamondLoupeFacet.sol"; +import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; +import {DiamondInterface} from "@compose-protocol/diamond-interface/DiamondInterface.sol"; -contract Consumer { - address constant DIAMOND_ADDRESS = 0x1234567890abcdef1234567890abcdef1234567890; +contract ERC20PermitConsumer { + DiamondInterface public diamond; - // Assuming ERC20PermitFacet is added to the diamond - // and its selector is known. - function consumePermit(address tokenAddress, address spender, uint256 amount, uint256 deadline, bytes calldata signature) external { - // Get the ERC20PermitFacet interface - IERC20Permit permitFacet = IERC20Permit(DIAMOND_ADDRESS); + constructor(address _diamondAddress) { + diamond = DiamondInterface(_diamondAddress); + } + + /** + * @notice Get the current nonce for a given owner. + * @param _owner The address of the token owner. + * @return The current nonce. + */ + function getUserNonce(address _owner) external view returns (uint256) { + // The selector for nonces function from ERC20PermitFacet + bytes4 selector = bytes4(keccak256("nonces(address)")); + return abi.decode(diamond.callStatic(selector, abi.encode(_owner)), (uint256)); + } - // Call the permit function on the diamond - // Note: The actual diamond implementation will route this to the ERC20PermitFacet. - permitFacet.permit(tokenAddress, msg.sender, spender, amount, deadline, signature); + /** + * @notice Set an allowance using EIP-2612 permit. + * @param _owner The address of the token owner. + * @param _spender The address to grant allowance to. + * @param _value The amount to allow. + * @param _deadline The permit deadline. + * @param _v The signature v component. + * @param _r The signature r component. + * @param _s The signature s component. + */ + function permitToken(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { + // The selector for permit function from ERC20PermitFacet + bytes4 selector = bytes4(keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")); + diamond.execute(selector, abi.encode(_owner, _spender, _value, _deadline, _v, _r, _s)); } }`} @@ -308,18 +329,19 @@ contract Consumer { ## Best Practices -- Integrate the ERC20PermitFacet into your diamond to leverage EIP-2612 permit functionality for gas-efficient approvals. -- Ensure the `permit` function is correctly called with a valid EIP-712 compliant signature, including the correct domain separator and nonce. +- Ensure the ERC20PermitFacet is correctly initialized with the appropriate domain separator and chain ID during diamond deployment. +- Validate the permit deadline to prevent expired permits from being used. +- Use the `nonces` function to track the usage of permits and prevent replay attacks. ## Security Considerations -The `permit` function is susceptible to signature replay attacks if the `DOMAIN_SEPARATOR` is not unique per chain and contract, or if nonces are not properly managed. Ensure that the nonce for each owner is incremented after each successful permit usage. The `ERC2612InvalidSignature` and `ERC20InvalidSpender` errors are emitted on invalid signatures or incorrect spender addresses, respectively. +This facet implements EIP-2612, which relies on cryptographic signatures. Ensure that the signature verification process is robust and that the domain separator is correctly configured to prevent replay attacks across different contracts or chains. Input validation on `_deadline` is crucial to prevent the use of expired permits.
- + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx index 9d2c1ad7..4137a99b 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20PermitMod" -description: "Implement ERC-2612 permit functionality for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +description: "ERC-2612 Permit logic and domain separator" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Implement ERC-2612 permit functionality for ERC-20 tokens. +ERC-2612 Permit logic and domain separator -- Implements EIP-2612 Permit functionality for ERC-20 tokens. -- Provides a reusable library for domain separator generation and signature verification. -- Isolates complex signature logic, enabling cleaner facet implementations. +- Implements ERC-2612 permit functionality for gasless allowance grants. +- Manages the domain separator for signature validation, ensuring chain and contract uniqueness. +- Provides a standardized interface for permit operations. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the core logic and storage for implementing the ERC-2612 Permit functionality. It allows users to grant token allowances via signed messages, enhancing gas efficiency and user experience. By isolating this logic, facets can easily integrate permit capabilities without duplicating complex signature verification and allowance setting code. +This module provides the necessary logic and storage structures for implementing ERC-2612 permit functionality. It allows users to grant allowances via signed messages, enhancing gas efficiency and user experience. Integrating this module enables standard permit flows within your diamond. --- @@ -48,7 +48,7 @@ storage-location: erc8042:compose.erc20.permit {`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; + mapping(address owner => uint256) nonces; }`} @@ -59,11 +59,11 @@ storage-location: erc8042:compose.erc20 {`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; }`} @@ -236,57 +236,43 @@ address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, b {`pragma solidity ^0.8.30; -import {IERC20PermitMod, IERC20PermitStorage} from "@compose/modules/erc20/ERC20PermitMod.sol"; +import {IERC20PermitMod, ERC20InvalidSpender, ERC2612InvalidSignature} from "@compose/modules/erc20-permit/src/ERC20PermitMod.sol"; contract MyERC20Facet { - // Assume ERC20PermitMod is deployed and its address is known - address immutable erc20PermitModAddress; + using ERC20PermitMod for ERC20PermitMod.PermitStorage; - constructor(address _erc20PermitModAddress) { - erc20PermitModAddress = _erc20PermitModAddress; + ERC20PermitMod.PermitStorage public permitStorage; + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Ensure this function is callable and correctly routed within the diamond. + // The module's permit function will validate the signature and update allowances. + permitStorage.permit(owner, spender, value, deadline, v, r, s); } - /** - * @notice Allows a user to permit an operator by signing a permit message. - * @param _owner The owner of the tokens. - * @param _spender The address to grant allowance to. - * @param _value The amount of tokens to allow. - * @param _deadline The timestamp after which the permit is invalid. - * @param _v The v component of the signature. - * @param _r The r component of the signature. - * @param _s The s component of the signature. - */ - function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { - // Call the module's permit function. The module will handle signature validation - // and setting the allowance. The Approval event MUST be emitted by the calling facet. - (bool success, bytes memory data) = erc20PermitModAddress.call(abi.encodeWithSelector(IERC20PermitMod.permit.selector, _owner, _spender, _value, _deadline, _v, _r, _s)); - require(success, "ERC20PermitMod: permit call failed"); - - // Emit the Approval event as required by ERC-20 and ERC-2612 - emit Approval(_owner, _spender, _value); + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return permitStorage.DOMAIN_SEPARATOR(); } - // Other ERC-20 functions... -} -`} + // Other ERC20 functions like allowance, approve, etc. +}`} ## Best Practices -- Ensure the `permit` function in your facet emits the `Approval` event after a successful call to the module, as required by the ERC-2612 standard. -- Validate the `_deadline` parameter to prevent the use of stale permits. -- Implement appropriate access control for the `permit` function if necessary, though it is typically permissionless. +- Ensure the `permit` function is correctly accessible through the diamond proxy. +- The `Approval` event must be emitted by the facet calling the module's `permit` function, not the module itself. +- Validate `deadline` to prevent stale permits. ## Integration Notes -This module requires access to specific storage slots for ERC-20 token data and permit-specific data. The `getERC20Storage` and `getPermitStorage` functions are internal helpers to access these slots. Facets integrating this module must ensure that their storage layout is compatible or that they correctly delegate to this module's storage access. The `permit` function in the calling facet must emit the `Approval` event to adhere to ERC-20 and ERC-2612 standards. +This module manages its own `PermitStorage` struct. Facets integrating this module should declare a `PermitStorage` variable and use the `ERC20PermitMod` library to interact with it. The `permit` function within the module validates the signature and updates allowances. The `Approval` event, however, must be emitted by the calling facet after the module's `permit` function successfully executes. The `DOMAIN_SEPARATOR` function should be exposed by the facet to allow clients to construct valid permit messages.
- + diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx index 9fc0a035..c546197b 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/index.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/index.mdx @@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx index d3552b4b..05611fc3 100644 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC6909Facet" -description: "Manages fungible and non-fungible tokens via ERC-6909." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +description: "Manage ERC-6909 tokens and operator roles." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC6909/ERC6909/ERC6909Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages fungible and non-fungible tokens via ERC-6909. +Manage ERC-6909 tokens and operator roles. -- Implements the ERC-6909 standard for token management. -- Supports both fungible and non-fungible token types through a unified interface. -- Provides functions for direct transfers, allowance management, and operator delegation. +- Implements the ERC-6909 token standard interface. +- Supports both direct transfers and transfers via approved operators. +- Manages explicit operator approvals for token IDs. ## Overview -The ERC6909Facet implements the ERC-6909 standard for managing fungible and non-fungible assets within a Compose diamond. It provides core functionalities for checking balances, allowances, and managing operator permissions, enabling flexible token operations. +The ERC6909Facet implements the ERC-6909 standard, enabling token transfers and operator management within a Compose diamond. It provides functions for checking balances, allowances, and managing operator approvals, facilitating composable token interactions. --- @@ -481,22 +481,26 @@ error ERC6909InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC6909 } from "@compose-chain/contracts/src/interfaces/IERC6909.sol"; -import { ERC6909Facet } from "@compose-chain/contracts/src/facets/ERC6909Facet.sol"; +import {IDiamondCut} from "@compose/diamond-proxy/src/interfaces/IDiamondCut.sol"; +import {IERC6909Facet} from "./interfaces/IERC6909Facet.sol"; -contract Consumer { - address diamondAddress; +contract Deployer { + address immutable diamondAddress; - function transferToken(uint256 _id, uint256 _amount, address _to) external { - IERC6909(diamondAddress).transfer(_id, _amount, _to); + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; } - function approveToken(uint256 _id, uint256 _amount, address _spender) external { - IERC6909(diamondAddress).approve(_id, _amount, _spender); + function grantOperator(address _operator, uint256 _tokenId) external { + bytes4 selector = IERC6909Facet.setOperator.selector; + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _operator, true, _tokenId)); + require(success, "Failed to grant operator"); } - function balanceOfToken(uint256 _id, address _owner) external view returns (uint256) { - return IERC6909(diamondAddress).balanceOf(_id, _owner); + function transferTokens(address _to, uint256 _tokenId, uint256 _amount) external { + bytes4 selector = IERC6909Facet.transfer.selector; + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _to, _tokenId, _amount)); + require(success, "Failed to transfer tokens"); } }`} @@ -504,19 +508,19 @@ contract Consumer { ## Best Practices -- Initialize the ERC6909Facet with necessary parameters during diamond deployment. -- Ensure appropriate access controls are implemented at the diamond level for sensitive operations like `setOperator`. -- Store token data in a manner that aligns with the ERC-6909 storage layout to ensure compatibility and upgradeability. +- Initialize operator roles and token approvals using the `setOperator` and `approve` functions respectively. +- Utilize `transfer` and `transferFrom` for token movements, ensuring sufficient balances and allowances are met. +- Access the facet's internal storage pointer via `getStorage` for advanced diagnostics or custom logic integration. ## Security Considerations -Implement strict access controls on the diamond proxy level to prevent unauthorized calls to `setOperator`. Ensure that all token transfers and approvals adhere to the standard ERC-6909 logic to prevent reentrancy attacks and maintain state integrity. Validate all input parameters to prevent unexpected behavior or state corruption. +Ensure that `approve` and `setOperator` calls are appropriately authorized by the token owner. `transfer` and `transferFrom` will revert on insufficient balance or allowance, preventing unauthorized token movements. Input validation is handled internally by the facet to prevent invalid receiver or sender addresses.
- + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx index 61f31fac..12557577 100644 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC6909Mod" -description: "Implements ERC-6909 minimal multi-token logic." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +description: "Implements ERC-6909 minimal multi-token functionality." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC6909/ERC6909/ERC6909Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Implements ERC-6909 minimal multi-token logic. +Implements ERC-6909 minimal multi-token functionality. -- Supports minting, burning, transferring, and approving tokens by ID. -- Includes operator functionality for delegated transfers. -- Provides necessary storage layout and internal functions for ERC-6909 compliance. +- Supports standard ERC-6909 token operations (transfer, approve, burn, mint). +- Enables operator functionality to bypass allowance checks. +- Provides direct access to internal storage via `getStorage` for advanced interactions. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC6909Mod provides the core logic and storage for implementing the ERC-6909 standard within a Compose diamond. It enables standard token operations like minting, burning, transferring, and approvals for multiple token IDs, ensuring composability and adherence to the standard. +This module provides the core logic and storage for the ERC-6909 standard, enabling diamonds to manage multiple token types with standard transfer, approval, and operator functionalities. It adheres to Compose's storage pattern for safe upgrades and composability. --- @@ -48,9 +48,9 @@ storage-location: erc8042:compose.erc6909 {`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; }`} @@ -477,38 +477,59 @@ error ERC6909InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC6909, IERC6909Mod} from "@compose/contracts/src/modules/erc6909/interfaces/IERC6909.sol"; -import {ERC6909} from "@compose/contracts/src/modules/erc6909/facets/ERC6909.sol"; +import {IERC6909Mod} from "./interfaces/IERC6909Mod.sol"; -contract MyERC6909Facet is ERC6909 { - function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { - // Assume ownership checks or other logic is handled externally - transfer(_from, _to, _id, _amount); +contract MyERC6909Facet { + address immutable DIAMOND_ADDRESS; + IERC6909Mod private constant _erc6909 = IERC6909Mod(DIAMOND_ADDRESS); + + // ... other facet storage + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; } - function approveToken(address _spender, uint256 _id, uint256 _amount) external { - // Assume ownership checks or other logic is handled externally - approve(_spender, _id, _amount); + /** + * @notice Transfers tokens from the caller. + * @param _id The token ID to transfer. + * @param _from The address to transfer from. + * @param _to The address to transfer to. + * @param _amount The amount to transfer. + */ + function transferFrom(uint256 _id, address _from, address _to, uint256 _amount) external { + _erc6909.transfer(_id, _from, _to, _amount); } + + /** + * @notice Approves a spender for a token ID. + * @param _id The token ID. + * @param _spender The address to approve. + * @param _amount The amount to approve. + */ + function approve(uint256 _id, address _spender, uint256 _amount) external { + _erc6909.approve(_id, _spender, _amount); + } + + // ... other functions }`} ## Best Practices -- Ensure proper access control is implemented in calling facets for sensitive operations like minting and burning. -- Be mindful of allowance management; `type(uint256).max` bypasses deduction, and operators can transfer without deduction. -- Handle potential `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` errors gracefully in calling facets. +- Ensure the `ERC6909Mod` facet is correctly initialized with the appropriate storage slot. +- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` gracefully in your facet logic. +- Be aware that operators bypass allowance checks for transfers, so manage operator roles carefully. ## Integration Notes -The ERC6909Mod defines a specific storage struct (`ERC6909Storage`) which must be laid out according to the Compose storage pattern. Facets interacting with this module should use the `getStorage` function (or access the storage slot directly if integrated via an initializer) to retrieve a pointer to this struct. Operations modify this shared storage, making them visible to all facets that implement ERC-6909 logic. +The `ERC6909Mod` relies on a dedicated storage slot for its internal `ERC6909Storage` struct. Facets interacting with this module should use the `getStorage` function to obtain a pointer to this struct. Ensure the `ERC6909Mod` facet is added to the diamond and its functions are correctly routed. The module's operations directly modify the shared diamond storage.
- + diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx index 3b2e2209..496e968e 100644 --- a/website/docs/library/token/ERC6909/ERC6909/index.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/index.mdx @@ -14,14 +14,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx index 514cf687..46db8dc2 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721BurnFacet" -description: "Burn ERC-721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +description: "Burn ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721/ERC721BurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC-721 tokens within a Compose diamond. +Burn ERC721 tokens within a Compose diamond. -- Enables destruction of ERC-721 tokens. -- Integrates with diamond storage for efficient state management. -- Emits standard ERC-721 `Transfer` event upon successful burn. +- Allows burning of ERC721 tokens, permanently removing them from circulation. +- Emits standard `Transfer` events with `from` and `to` addresses set to the zero address, as per ERC721 standards for token destruction. +- Integrates seamlessly with the Compose diamond pattern for composable functionality. ## Overview -The ERC721BurnFacet provides the functionality to destroy ERC-721 tokens. It integrates with the diamond's storage pattern to efficiently manage token state during the burn process, ensuring compliance with ERC-721 standards. +The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens. It interacts directly with the diamond's storage to remove tokens from the ERC721 contract's state, emitting standard ERC721 events. --- @@ -170,21 +170,25 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721BurnFacet} from "../facets/ERC721BurnFacet.sol"; -import {IDiamondCut} from "../diamond/IDiamondCut.sol"; +import {IERC721BurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/IERC721BurnFacet.sol"; +import {DiamondProxy} from "@compose-protocol/diamond-contracts/contracts/DiamondProxy.sol"; -contract Deployer { - address public diamondAddress; +contract ERC721BurnExample { + DiamondProxy public diamondProxy; - function deploy() public { - // ... diamond deployment logic ... - diamondAddress = address(0xYourDiamondAddress); + // Replace with actual diamond address + address immutable DIAMOND_ADDRESS = address(0x1234567890abcdef); + + constructor() { + diamondProxy = DiamondProxy(DIAMOND_ADDRESS); } - function burnToken(address _toBurnToken, uint256 _tokenId) public { - IERC721BurnFacet burnFacet = IERC721BurnFacet(diamondAddress); - // Ensure sufficient approval or ownership before burning - burnFacet.burn(_toBurnToken, _tokenId); + function burnToken(uint256 tokenId) public { + bytes4 selector = IERC721BurnFacet.burn.selector; + // abi.encodeWithSignature is not used here as it is a direct call to the diamond proxy + // The diamond proxy will route the call to the correct facet based on the selector + (bool success, bytes memory data) = diamondProxy.diamondCall(abi.encodeWithSelector(selector, tokenId)); + require(success, "ERC721BurnFacet: burn failed"); } }`} @@ -192,19 +196,18 @@ contract Deployer { ## Best Practices -- Ensure the `ERC721BurnFacet` is correctly cut into the diamond, pointing to the appropriate contract address. -- Before calling `burn`, verify that the caller has the necessary permissions (owner of the token or approved by the owner). -- Handle `ERC721NonexistentToken` and `ERC721InsufficientApproval` errors appropriately. +- Ensure the `ERC721BurnFacet` is correctly registered with the diamond proxy. +- Verify that the caller has the necessary approvals or ownership to burn the specified token before invoking the `burn` function. ## Security Considerations -The `burn` function should only be callable by the token owner or an address approved by the token owner. The facet relies on the underlying ERC-721 implementation for ownership and approval checks. Access control for calling the `burn` function on the diamond proxy itself is managed by the diamond's access control mechanism. +The `burn` function must be called by an address that is the owner of the token or has been explicitly approved to burn the token. Ensure proper access control mechanisms are in place at the diamond level or within the calling contract to enforce these conditions. The facet itself does not implement additional access control beyond what is expected by the ERC721 standard.
- + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx index 36bfa34d..8df692c7 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721Facet" -description: "Manages ERC-721 token ownership, approvals, and metadata within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721/ERC721Facet.sol" +description: "Manages ERC-721 token standard operations within a diamond." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721/ERC721Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-721 token ownership, approvals, and metadata within a diamond. +Manages ERC-721 token standard operations within a diamond. -- Full ERC-721 compliance, including safe transfer hooks. -- Direct access to token metadata via `tokenURI`. -- Comprehensive ownership and approval management functions. -- Internal `internalTransferFrom` for composable internal logic. +- Implements core ERC-721 functions: `name`, `symbol`, `tokenURI`, `balanceOf`, `ownerOf`, `getApproved`, `isApprovedForAll`. +- Supports token transfers via `transferFrom` and `safeTransferFrom` (with and without data). +- Manages approvals for individual tokens and for all tokens owned by an address. ## Overview -The ERC721Facet provides a complete implementation of the ERC-721 Non-Fungible Token Standard. It enables token creation, transfers, ownership tracking, and approval management, surfacing these critical functionalities through the diamond proxy's unified interface. +The ERC721Facet provides the core functionality for managing non-fungible tokens (NFTs) on-chain, adhering to the ERC-721 standard. It handles token ownership, transfers, approvals, and metadata retrieval, enabling composability for NFT marketplaces and games within a Compose diamond. --- @@ -620,12 +619,10 @@ error ERC721InvalidOperator(address _operator); import {IERC721Facet} from "@compose/contracts/src/facets/ERC721Facet.sol"; contract ERC721Consumer { - address immutable DIAMOND_FACET_CUTTER; - IERC721Facet immutable erc721Facet; + IERC721Facet public immutable erc721Facet; - constructor(address _diamondFacetCutter) { - DIAMOND_FACET_CUTTER = _diamondFacetCutter; - erc721Facet = IERC721Facet(DIAMOND_FACET_CUTTER); + constructor(address _erc721FacetAddress) { + erc721Facet = IERC721Facet(_erc721FacetAddress); } function getTokenName() external view returns (string memory) { @@ -636,12 +633,13 @@ contract ERC721Consumer { return erc721Facet.symbol(); } - function getTokenOwner(uint256 tokenId) external view returns (address) { + function getOwner(uint256 tokenId) external view returns (address) { return erc721Facet.ownerOf(tokenId); } - function safeTransferToken(address from, address to, uint256 tokenId) external { - erc721Facet.safeTransferFrom(from, to, tokenId); + function transferToken(address from, address to, uint256 tokenId) external { + // Ensure appropriate approvals or ownership before calling + erc721Facet.transferFrom(from, to, tokenId); } }`}
@@ -649,19 +647,19 @@ contract ERC721Consumer { ## Best Practices -- Ensure the ERC721Facet is correctly initialized with its storage slot and any required parameters during diamond deployment. -- Utilize the `ownerOf`, `balanceOf`, and `getApproved` functions to query token state before performing state-changing operations. -- Implement robust access control mechanisms for functions that mint or burn tokens if these capabilities are exposed through other facets. +- Initialize the `ERC721Facet` with correct storage slot configurations during diamond deployment. +- Ensure proper access control is implemented at the diamond level or within calling facets for sensitive operations like transfers and approvals. +- Leverage `safeTransferFrom` when interacting with potentially unknown receiver contracts to ensure ERC-721 compliance. ## Security Considerations -Input validation is crucial for all token-related operations, especially `tokenId`. Ensure that token IDs exist before attempting transfers or approvals to prevent `ERC721NonexistentToken` errors. The `safeTransferFrom` functions include checks for receiver compatibility, mitigating reentrancy risks related to token handling. Access to sensitive functions like minting or burning (if applicable through other facets) must be strictly controlled. +Ensure that calls to `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` are guarded by appropriate access control mechanisms to prevent unauthorized actions. The internal `internalTransferFrom` function includes necessary checks for ownership and approvals, but the facet itself does not enforce external authorization beyond ERC-721 standard requirements.
- + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx index 7a4a0d53..9ae8674b 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721Mod" -description: "Manage ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721/ERC721Mod.sol" +description: "Internal logic for ERC-721 token management." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721/ERC721Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage ERC721 tokens within a Compose diamond. +Internal logic for ERC-721 token management. -- Implements core ERC-721 operations: mint, burn, and transfer. -- Utilizes inline assembly for efficient access to diamond storage. -- Enforces standard ERC-721 ownership and approval checks. +- Provides internal, reusable logic for minting, burning, and transferring ERC-721 tokens. +- Leverages diamond storage pattern for efficient and consistent state management. +- Includes necessary error handling for common ERC-721 operations. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC721Mod provides essential internal logic for ERC-721 token management, enabling custom facets to integrate robust token functionalities. It abstracts the complexities of ERC-721 state handling, ensuring safe and consistent token operations within the diamond's storage. +This module provides the core internal logic for managing ERC-721 tokens within a Compose diamond. It encapsulates essential functions like minting, burning, and transferring, ensuring consistency and efficient state management through the diamond's storage pattern. By using this module, facets can reliably integrate ERC-721 functionality without reimplementing complex state logic. --- @@ -46,13 +46,13 @@ The ERC721Mod provides essential internal logic for ERC-721 token management, en {`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; }`} @@ -314,25 +314,37 @@ error ERC721NonexistentToken(uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721Mod } from "@compose/modules/erc721/IERC721Mod.sol"; +import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; contract MyERC721Facet { - IERC721Mod internal erc721Mod; + struct DiamondStorage { + // ... other storage ... + IERC721Mod.ERC721Storage erc721; + } - constructor(address _diamondProxy) { - erc721Mod = IERC721Mod(_diamondProxy); + function _getERC721Storage() internal pure returns (IERC721Mod.ERC721Storage storage _erc721) { + assembly (memory-safe) { + // Slot 1 is reserved for ERC721Storage + _erc721 := sload(1) + } } function mintToken(address _to, uint256 _tokenId) external { - erc721Mod.mint(_to, _tokenId); + IERC721Mod.ERC721Storage storage erc721Storage = _getERC721Storage(); + // Call the internal mint function from the module + IERC721Mod.mint(erc721Storage, _to, _tokenId); } function burnToken(uint256 _tokenId) external { - erc721Mod.burn(_tokenId); + IERC721Mod.ERC721Storage storage erc721Storage = _getERC721Storage(); + // Call the internal burn function from the module + IERC721Mod.burn(erc721Storage, _tokenId); } function transferToken(address _from, address _to, uint256 _tokenId) external { - erc721Mod.transferFrom(_from, _to, _tokenId); + IERC721Mod.ERC721Storage storage erc721Storage = _getERC721Storage(); + // Call the internal transferFrom function from the module + IERC721Mod.transferFrom(erc721Storage, _from, _to, _tokenId); } }`} @@ -340,19 +352,19 @@ contract MyERC721Facet { ## Best Practices -- Ensure the `ERC721Mod` facet is correctly initialized with the diamond proxy address. -- Always validate token existence and ownership before calling `transferFrom` or `burn` if external checks are needed beyond the module's internal reverts. -- Handle custom errors like `ERC721IncorrectOwner` and `ERC721NonexistentToken` in facet logic for clear user feedback. +- Ensure the ERC721Storage struct is correctly placed in a dedicated storage slot (e.g., slot 1) and accessed via inline assembly for predictable state management. +- Implement access control within your facets to restrict who can call mint, burn, and transfer functions, as these operations modify critical token ownership data. +- Handle custom errors like `ERC721IncorrectOwner` and `ERC721NonexistentToken` gracefully in your facet logic to provide informative feedback to users. ## Integration Notes -The ERC721Mod interacts with diamond storage using a predefined slot for its `ERC721Storage` struct. Facets using this module will access and modify token ownership, approvals, and metadata through the module's functions. Changes made by the module are immediately visible to all facets via the diamond proxy. The storage layout is defined within the module and should not be altered by other facets to maintain compatibility. +The `ERC721Mod` module utilizes a dedicated storage slot (conventionally slot 1) for its `ERC721Storage` struct. Facets integrating with this module must ensure this storage slot is allocated and accessible. The `getStorage` function demonstrates how to access this storage using inline assembly. Any facet that interacts with ERC-721 state must retrieve this storage struct and pass it to the module's internal functions. Changes made via the module's functions are immediately reflected in the diamond's storage and visible to all facets.
- + diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx index 7ead6277..74f7eaa2 100644 --- a/website/docs/library/token/ERC721/ERC721/index.mdx +++ b/website/docs/library/token/ERC721/ERC721/index.mdx @@ -14,21 +14,21 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx index b79bd297..4a4a66b2 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableBurnFacet" -description: "Burn ERC721 tokens and manage enumeration." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +description: "Burn tokens and manage ERC721 enumeration." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,17 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC721 tokens and manage enumeration. +Burn tokens and manage ERC721 enumeration. -- Allows burning of ERC721 tokens. -- Integrates with enumeration tracking to remove burned tokens. -- Emits `Transfer` event upon successful token burn. +- Burns ERC721 tokens, permanently removing them from circulation. +- Maintains enumeration order by removing burned tokens from internal tracking. ## Overview -The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens within a Compose diamond. It ensures that burned tokens are correctly removed from enumeration tracking, maintaining the integrity of the token supply and ownership lists. +This facet provides functionality to burn ERC721 tokens and updates internal enumeration tracking. It ensures that burned tokens are removed from the total supply and ownership lists, maintaining the integrity of enumerable ERC721 state. --- @@ -185,19 +184,17 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721EnumerableBurnFacet} from "../facets/IERC721EnumerableBurnFacet.sol"; +import {IERC721EnumerableBurn} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/ERC721EnumerableBurn.sol"; -contract MyDiamondConsumer { - IERC721EnumerableBurnFacet immutable erc721BurnFacet; +contract ExampleConsumer { + address immutable diamondProxy; - constructor(address _diamondAddress) { - // Assume _diamondAddress is the address of your deployed diamond proxy - erc721BurnFacet = IERC721EnumerableBurnFacet(_diamondAddress); + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; } - function burnToken(uint256 _tokenId) public { - // Directly call the burn function on the diamond proxy - erc721BurnFacet.burn(_tokenId); + function burnToken(uint256 _tokenId) external { + IERC721EnumerableBurn(diamondProxy).burn(_tokenId); } }`} @@ -205,19 +202,18 @@ contract MyDiamondConsumer { ## Best Practices -- Ensure the `ERC721EnumerableBurnFacet` is correctly registered with the diamond proxy before use. -- Always verify token ownership and necessary approvals before calling the `burn` function to prevent `ERC721InsufficientApproval` errors. -- Consider the implications of burning on token enumeration order and total supply. +- Ensure the `ERC721EnumerableBurnFacet` is correctly initialized with the diamond proxy. +- Verify that the caller has the necessary permissions to burn the specified token, as enforced by the `burn` function's access control. ## Security Considerations -This facet's `burn` function requires careful access control to be implemented at the diamond level or within calling contracts to ensure only authorized parties can burn tokens. The function itself does not perform ownership checks; these must be handled by the caller or enforced by other facets. Incorrect usage could lead to unintended token destruction. The `ERC721NonexistentToken` error is emitted if the token ID does not exist. +The `burn` function requires careful access control to ensure only authorized parties can destroy tokens. The facet relies on the underlying ERC721 implementation to enforce ownership checks before burning. Ensure the `Transfer` event is correctly emitted by the facet implementation for accurate off-chain tracking.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx index 72b92335..58cefd7d 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableFacet" -description: "Enumerable ERC-721 token management and querying." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +description: "Enumerable ERC721 token management" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Enumerable ERC-721 token management and querying. +Enumerable ERC721 token management -- Full ERC-721 functionality with enumeration. -- `tokenOfOwnerByIndex` enables querying tokens by owner and index. -- Supports `safeTransferFrom` for secure token transfers to contract recipients. +- Provides standard ERC721 functions (`name`, `symbol`, `ownerOf`, `balanceOf`, `approve`, `transferFrom`, `safeTransferFrom`). +- Includes enumerable features (`totalSupply`, `tokenOfOwnerByIndex`) for efficient collection querying. +- Supports metadata via `tokenURI`. +- Implements robust access control and error handling for token operations. ## Overview -This facet provides comprehensive ERC-721 compliant functionality, including token enumeration capabilities. It allows querying token names, symbols, URIs, total supply, individual token ownership, balances, and approvals. The `tokenOfOwnerByIndex` function is key for iterating through a user's tokens. +This facet provides full ERC721 functionality with enumerable extensions, enabling efficient querying of token ownership, supply, and individual token IDs by index. It integrates seamlessly into a Compose diamond, expanding its NFT capabilities. --- @@ -690,30 +691,27 @@ error ERC721OutOfBoundsIndex(address _owner, uint256 _index); {`pragma solidity ^0.8.30; -import {IERC721EnumerableFacet} from "@compose-protocol/diamond/contracts/facets/ERC721/IERC721EnumerableFacet.sol"; +import {IERC721EnumerableFacet} from "@compose-protocol/diamond/facets/ERC721/IERC721EnumerableFacet.sol"; -contract ERC721EnumerableConsumer { +contract MyDiamondUser { IERC721EnumerableFacet immutable erc721Facet; - constructor(address _diamondAddress) { - erc721Facet = IERC721EnumerableFacet(_diamondAddress); + constructor(address diamondAddress) { + // Assuming the diamond proxy is deployed and initialized + // and the ERC721EnumerableFacet is added and selectors are routed. + erc721Facet = IERC721EnumerableFacet(diamondAddress); } function getTokenSupply() external view returns (uint256) { return erc721Facet.totalSupply(); } - function getTokenOwner(uint256 _tokenId) external view returns (address) { - return erc721Facet.ownerOf(_tokenId); + function getTokenOwner(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); } - function getTokensOwned(address _owner) external view returns (uint256[] memory) { - uint256 count = erc721Facet.balanceOf(_owner); - uint256[] memory tokenIds = new uint256[](count); - for (uint256 i = 0; i < count; i++) { - tokenIds[i] = erc721Facet.tokenOfOwnerByIndex(_owner, i); - } - return tokenIds; + function getOwnedTokenId(address owner, uint256 index) external view returns (uint256) { + return erc721Facet.tokenOfOwnerByIndex(owner, index); } }`} @@ -721,19 +719,19 @@ contract ERC721EnumerableConsumer { ## Best Practices -- Use `tokenOfOwnerByIndex` carefully for large token balances, as it requires multiple calls and can be gas-intensive. -- Ensure proper access control is implemented at the diamond level for functions like `approve` and `transferFrom`. -- When upgrading the diamond, ensure the storage layout of this facet remains compatible. +- Ensure the `ERC721EnumerableFacet` is correctly initialized with a unique storage slot upon deployment. +- Route `IERC721EnumerableFacet` selectors to this facet within the diamond proxy's facet address array. +- Use `internalTransferFrom` for internal state transitions to maintain data integrity. ## Security Considerations -The `internalTransferFrom` function is intended for internal use by the facet itself and should not be directly exposed. Ensure that external calls to `transferFrom` and `safeTransferFrom` are properly validated for sender and receiver permissions. `approve` and `setApprovalForAll` require caller authorization. Reentrancy is mitigated by standard ERC721 patterns and the diamond proxy architecture. +This facet handles critical token ownership and transfer logic. Ensure proper access control is enforced by the diamond proxy's security layer. Input validation is performed by custom errors to prevent invalid operations. Reentrancy is mitigated through internal state management and checks before external calls where applicable. Be mindful of state coupling if other facets interact with token ownership or approvals.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx index 054a744d..bbdedfc5 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableMod" -description: "Manages enumerable ERC-721 tokens within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +description: "Manages ERC721 enumerable token state and operations." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages enumerable ERC-721 tokens within a diamond. +Manages ERC721 enumerable token state and operations. -- Manages token IDs for enumeration (e.g., `tokenOfOwnerByIndex`, `tokenByIndex`). -- Integrates seamlessly with diamond storage via predefined slots. -- Provides core ERC-721 transfer, mint, and burn logic with enumeration updates. +- Manages the enumeration of ERC721 tokens, including token counts and order. +- Provides atomic operations for minting and burning tokens, updating enumerable state. +- Integrates seamlessly with diamond storage via its `getStorage` function for state retrieval. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC721EnumerableMod provides the core logic for managing enumerable ERC-721 tokens. It ensures that tokens are correctly added to and removed from internal tracking structures upon minting, burning, and transferring. This module enables facets to implement full ERC-721 compliance with token enumeration capabilities. +The ERC721EnumerableMod provides essential internal logic for managing enumerable ERC721 tokens within a Compose diamond. It handles token minting, burning, and transfers while maintaining accurate counts and ownership records, crucial for compliant ERC721 implementations. --- @@ -46,16 +46,16 @@ The ERC721EnumerableMod provides the core logic for managing enumerable ERC-721 {`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; }`} @@ -297,28 +297,31 @@ error ERC721NonexistentToken(uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721EnumerableMod, IERC721EnumerableStorage} from "@compose/modules/erc721/ERC721EnumerableMod.sol"; +import {IERC721EnumerableMod} from "@compose/modules/ERC721EnumerableMod.sol"; contract MyERC721Facet { - IERC721EnumerableMod internal immutable erc721EnumerableMod; + // Assume IERC721EnumerableMod is initialized and accessible + IERC721EnumerableMod public immutable erc721EnumerableMod; - constructor(address _diamondProxy) { - // Assume diamond proxy address is known - erc721EnumerableMod = IERC721EnumerableMod(_diamondProxy); + constructor(address _erc721EnumerableMod) { + erc721EnumerableMod = IERC721EnumerableMod(_erc721EnumerableMod); } function mintToken(address _to, uint256 _tokenId) external { - // Access storage via getStorage before minting to check existence if needed - // This example directly calls mint for simplicity erc721EnumerableMod.mint(_to, _tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - erc721EnumerableMod.transferFrom(_from, _to, _tokenId); + // Additional facet logic for minting } function burnToken(uint256 _tokenId) external { + // Assume ownership and approval checks are done here before calling burn erc721EnumerableMod.burn(_tokenId); + // Additional facet logic for burning + } + + function transferTokenFrom(address _from, address _to, uint256 _tokenId) external { + // Assume ownership and approval checks are done here before calling transferFrom + erc721EnumerableMod.transferFrom(_from, _to, _tokenId); + // Additional facet logic for transfers } }`} @@ -326,19 +329,19 @@ contract MyERC721Facet { ## Best Practices -- Ensure proper access control is implemented in facets calling this module's functions (e.g., minting, burning). -- Always validate token existence and ownership before attempting transfers or burns via facet logic. -- Be mindful of storage slot collisions if this module's storage layout is modified or extended. +- Ensure the ERC721EnumerableMod contract is properly initialized and accessible via its address. +- Implement robust access control within your facets to enforce ownership and approval rules before calling module functions like `transferFrom` and `burn`. +- Handle module-specific errors (e.g., `ERC721NonexistentToken`, `ERC721IncorrectOwner`) appropriately in facet logic. ## Integration Notes -This module relies on a specific storage slot for its `ERC721EnumerableStorage` struct. Facets interacting with this module should use the `getStorage` function to access the storage directly or call module functions which implicitly use it. The module's internal state is updated atomically during mint, burn, and transfer operations. No specific ordering is required for facet addition, but its storage slot must remain consistent. +The ERC721EnumerableMod utilizes a specific storage slot for its internal `ERC721EnumerableStorage` struct. Facets interacting with this module should be aware that `mint`, `burn`, and `transferFrom` directly modify this shared storage. The `getStorage` function allows facets to read this state directly using inline assembly, ensuring they have access to the most up-to-date enumerable token data.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx index 7eb0ae8a..41519edc 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx @@ -14,21 +14,21 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx index d3b3c340..210058a5 100644 --- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "RoyaltyFacet" description: "Manages token royalties according to ERC-2981." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/Royalty/RoyaltyFacet.sol" +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/Royalty/RoyaltyFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -26,14 +26,13 @@ Manages token royalties according to ERC-2981. - Implements ERC-2981 `royaltyInfo` function. -- Supports both default and token-specific royalty configurations. -- Calculates royalty fees as a percentage (basis points) of the sale price. -- Utilizes inline assembly for efficient storage access. +- Supports token-specific and default royalty configurations. +- Calculates royalty amounts based on sale price and basis points. ## Overview -The RoyaltyFacet implements the ERC-2981 standard for royalty payments. It provides functions to retrieve royalty information for a given token and sale price, supporting both token-specific and default royalty configurations. This facet enables marketplaces and other dApps to correctly distribute royalties. +The RoyaltyFacet implements the ERC-2981 standard to provide royalty information for NFTs. It allows setting token-specific royalties and a default royalty, ensuring creators receive their due percentage on secondary sales. This facet integrates seamlessly with the diamond proxy pattern for efficient storage and access. --- @@ -152,35 +151,21 @@ Returns royalty information for a given token and sale price. Returns token-spec {`pragma solidity ^0.8.30; -import {IDiamondCut, IDiamondLoupe} from "@compose/diamond-contracts/contracts/interfaces/IDiamond.sol"; -import {IRoyaltyFacet} from "@compose/diamond-contracts/contracts/facets/Royalty/IRoyaltyFacet.sol"; +import {IRoyaltyFacet} from "@compose/contracts/facets/Royalty/IRoyaltyFacet.sol"; -contract DeployDiamond { - // ... deployment setup ... +contract RoyaltyConsumer { + address immutable _diamondAddress; - function deploy() external { - // ... other facet deployments ... - - address royaltyFacet = address(new RoyaltyFacet()); - facetCuts.push(IDiamondCut.FacetCut({ - facetAddress: royaltyFacet, - action: IDiamondCut.Action.Add, - functionSelectors: - bytes4.concat(IRoyaltyFacet.royaltyInfo.selector) - })); - - // ... diamond cut execution ... + constructor(address diamondAddress) { + _diamondAddress = diamondAddress; } -} - -contract MyMarketplace { - // Assuming the diamond proxy is deployed at address \`diamondProxy\` - IRoyaltyFacet public royaltyFacet = IRoyaltyFacet(diamondProxy); - function getSaleRoyalty(uint256 tokenId, uint256 salePrice) public view returns (address receiver, uint256 feeBasisPoints) { - // Calls the royaltyInfo function through the diamond proxy - (receiver, feeBasisPoints) = royaltyFacet.royaltyInfo(tokenId, salePrice); - return (receiver, feeBasisPoints); + function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { + bytes4 selector = IRoyaltyFacet.royaltyInfo.selector; + (bool success, bytes memory data) = _diamondAddress.staticcall(abi.encodeWithSelector(selector, _tokenId, _salePrice)); + require(success, "RoyaltyFacet: call failed"); + (receiver, royaltyAmount) = abi.decode(data, (address, uint256)); + return (receiver, royaltyAmount); } }`} @@ -188,19 +173,19 @@ contract MyMarketplace { ## Best Practices -- Initialize royalty settings (default and token-specific) via a separate facet or deployment script after deploying the RoyaltyFacet. -- Ensure the `royaltyInfo` function is called through the diamond proxy to correctly dispatch the call to the facet. -- Store royalty data efficiently, considering the diamond storage pattern to avoid slot collisions. +- Initialize the facet with the default royalty receiver and basis points during diamond deployment. +- Use `setTokenRoyalty` to define specific royalties for individual tokens, overriding the default. +- Access royalty information via the diamond proxy to ensure correct routing and state management. ## Security Considerations -Access control for setting default and token-specific royalties should be managed by a separate facet (e.g., an ownership or admin facet). The `royaltyInfo` function itself is read-only and does not present reentrancy risks. Ensure the storage slot for royalty data is unique to prevent conflicts with other facets. +The `royaltyInfo` function is `view`, preventing reentrancy. Access control for setting royalties should be managed at the diamond level. Ensure the `_tokenId` and `_salePrice` inputs are validated appropriately by the calling facet or contract.
- + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx index c041dec5..a52a059d 100644 --- a/website/docs/library/token/Royalty/RoyaltyMod.mdx +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "RoyaltyMod" -description: "Manages ERC-2981 royalties for tokens and defaults." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/token/Royalty/RoyaltyMod.sol" +description: "Manages ERC-2981 royalty settings for tokens and defaults." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/Royalty/RoyaltyMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-2981 royalties for tokens and defaults. +Manages ERC-2981 royalty settings for tokens and defaults. -- Supports both default and token-specific royalty configurations. -- Implements ERC-2981 `royaltyInfo` function logic, including fallback to defaults. -- Provides functions to set, delete, and reset royalty information. +- Implements ERC-2981 `royaltyInfo` function logic. +- Supports setting both default and token-specific royalties. +- Provides functions to delete or reset royalty configurations. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides robust ERC-2981 royalty enforcement by managing both default and token-specific royalty configurations. It allows setting, retrieving, and deleting royalty information, ensuring compliance with the standard and enabling revenue sharing for NFTs. +The RoyaltyMod provides a robust implementation of the ERC-2981 royalty standard. It allows setting default royalties for all tokens and specific royalties for individual tokens, ensuring compliance and enabling revenue sharing for NFTs. This module is critical for marketplaces and secondary sales, offering a standardized way to distribute royalties. --- @@ -48,8 +48,8 @@ Structure containing royalty information. **Properties** {`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; + address receiver; + uint96 royaltyFraction; }`} @@ -60,8 +60,8 @@ storage-location: erc8042:compose.erc2981 {`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; }`} @@ -299,26 +299,27 @@ error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); {`pragma solidity ^0.8.30; -import {IRoyaltyMod} from "@compose/diamond-contracts/contracts/modules/royalty/IRoyaltyMod.sol"; -import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol"; +import {IRoyaltyMod} from "@compose-protocol/diamond-contracts/contracts/modules/royalty/interfaces/IRoyaltyMod.sol"; -contract MyFacet is IRoyaltyMod { - address immutable _diamondAddress; +contract RoyaltyFacet { + IRoyaltyMod internal royaltyMod; - constructor(address diamondAddress) { - _diamondAddress = diamondAddress; - } + // Assume royaltyMod is initialized externally - function _getRoyaltyMod() internal view returns (IRoyaltyMod) { - return IRoyaltyMod(_diamondAddress); + function setMyTokenRoyalty(uint256 tokenId, address payable receiver, uint16 feeNumerator, uint16 feeDenominator) external { + uint24 base = 10000; // ERC-2981 standard basis points denominator + uint16 feeBasisPoints = (feeNumerator * base) / feeDenominator; + royaltyMod.setTokenRoyalty(address(this), tokenId, receiver, feeBasisPoints); } - function exampleSetRoyalty(uint256 tokenId, address receiver, uint16 basisPoints) external { - _getRoyaltyMod().setTokenRoyalty(tokenId, receiver, basisPoints); + function getDefaultRoyalty() external view returns (address receiver, uint16 feeBasisPoints) { + bytes32 royaltyStorageSlot = IRoyaltyMod.ROYALTY_STORAGE_SLOT; + (address defaultReceiver, uint16 defaultFee) = royaltyMod.royaltyInfo(address(this), 0, 0); // tokenId 0 for default + return (defaultReceiver, defaultFee); } - function exampleRoyaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint16 basisPoints) { - return _getRoyaltyMod().royaltyInfo(tokenId, salePrice); + function deleteTokenRoyalty(uint256 tokenId) external { + royaltyMod.resetTokenRoyalty(address(this), tokenId); } }`} @@ -326,19 +327,19 @@ contract MyFacet is IRoyaltyMod { ## Best Practices -- Use `setDefaultRoyalty` for global royalty settings and `setTokenRoyalty` for specific exceptions. -- Validate receiver addresses and basis points before setting royalties to prevent errors. -- Be aware that `resetTokenRoyalty` will revert token-specific settings to the default. +- Use `setTokenRoyalty` to assign specific royalties per token, overriding defaults. +- Call `resetTokenRoyalty` to revert a token's royalty settings to the configured default. +- Validate receiver addresses and fee percentages rigorously before setting royalties to prevent errors and ensure compliance. ## Integration Notes -The RoyaltyMod utilizes a predefined storage slot to store its royalty configuration. The `getStorage` function provides direct access to this storage struct. Facets interacting with royalty logic should call the functions defined in `IRoyaltyMod` to ensure proper interaction with the diamond's storage and maintain consistency. +The RoyaltyMod utilizes a dedicated storage slot (`ROYALTY_STORAGE_SLOT`) for its state. The `royaltyInfo` function intelligently queries token-specific royalties first, falling back to default royalties if no specific setting is found for the given `tokenId`. The `deleteDefaultRoyalty` function effectively removes the default royalty information, causing `royaltyInfo` to return `(address(0), 0)` for tokens without specific settings. The `resetTokenRoyalty` function clears token-specific settings, enabling the fallback to default royalties.
- + diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx index 76b855c8..8e2d9ae8 100644 --- a/website/docs/library/token/Royalty/index.mdx +++ b/website/docs/library/token/Royalty/index.mdx @@ -21,7 +21,7 @@ import Icon from '@site/src/components/ui/Icon'; /> } size="medium" diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx index 6be28afc..27b81aee 100644 --- a/website/docs/library/utils/NonReentrancyMod.mdx +++ b/website/docs/library/utils/NonReentrancyMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "NonReentrancyMod" -description: "Prevent reentrant calls within facets." -gitSource: "https://github.com/maxnorm/Compose/blob/2dfa9eb69851162421010a7e56bd0fa891a9311a/src/libraries/NonReentrancyMod.sol" +description: "Prevent reentrant calls within diamond functions." +gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/libraries/NonReentrancyMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Prevent reentrant calls within facets. +Prevent reentrant calls within diamond functions. -- Provides `enter()` and `exit()` functions to manage reentrancy locks. -- Utilizes a dedicated storage slot for reentrancy state, ensuring isolation. -- Guards against reentrancy attacks by preventing recursive calls within protected functions. +- Provides `enter()` and `exit()` functions to manage a reentrancy lock. +- Uses a simple `uint256` as a flag for the reentrancy state, minimizing storage impact. +- Can be used as a library (`using LibNonReentrancy for uint256;`) within any facet. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The NonReentrancyMod provides essential functionality to prevent reentrant function calls within your diamond facets. By implementing checks before and after critical operations, it ensures the integrity of state transitions and guards against common reentrancy attacks. +The NonReentrancy module provides essential guards to prevent reentrant function calls, a common vulnerability in smart contracts. By integrating these functions into your facets, you ensure that sensitive operations are executed atomically and securely, maintaining the integrity of your diamond's state. --- @@ -96,21 +96,34 @@ error Reentrancy(); {`pragma solidity ^0.8.30; -import {LibNonReentrancy} from "./libraries/LibNonReentrancy.sol"; +import {LibNonReentrancy} from "@compose/modules/NonReentrancy/LibNonReentrancy.sol"; contract MyFacet { - using LibNonReentrancy for LibNonReentrancy.NonReentrancyStorage; + using LibNonReentrancy for uint256; - LibNonReentrancy.NonReentrancyStorage private _nonReentrancyStorage; + uint256 private _lock; - function doSomethingImportant() external { - // Acquire reentrancy lock - _nonReentrancyStorage.enter(); + /** + * @notice Performs a protected operation. + */ + function protectedOperation() external { + // Lock the reentrancy guard before execution. + _lock.enter(); - // ... perform state-changing operations ... + // Perform sensitive operations here... + // For example: interacting with external contracts, transferring tokens. - // Release reentrancy lock - _nonReentrancyStorage.exit(); + // Unlock the reentrancy guard after execution. + _lock.exit(); + } + + /** + * @notice Example of a function that might be called internally by a protected operation. + * This function should not be callable directly if reentrancy is a concern. + */ + function _internalOperation() internal { + // This function would be called within protectedOperation. + // It does not need to manage the lock itself, as the caller does. } }`} @@ -118,19 +131,19 @@ contract MyFacet { ## Best Practices -- Always call `enter()` at the beginning of a function that should not be reentrant. -- Always call `exit()` at the end of such a function, ensuring it is called even if an error occurs within the protected block. -- Use the `Reentrancy` custom error for clear and gas-efficient error handling. +- Always pair `enter()` with `exit()` to ensure the reentrancy lock is released, even in the event of an error before `exit()` is reached (e.g., using `try/catch` if necessary or ensuring `exit()` is the last statement before returning). +- Use `delete _lock;` in the facet's initializer to reset the lock state when the facet is deployed. +- Consider the scope of the lock; a single `uint256` variable is sufficient for one facet's reentrancy protection. ## Integration Notes -The `LibNonReentrancy` library manages its state within a `NonReentrancyStorage` struct. This struct should be declared and initialized in the facet that utilizes the library. The `enter` and `exit` functions operate on this storage, ensuring that reentrancy protection is specific to the facet's instance of the storage. The library itself does not introduce new diamond storage slots; it relies on the facet to manage its own storage. +This module is designed to be integrated directly into a facet's implementation. The `LibNonReentrancy` library operates on a `uint256` variable within the facet's storage. This variable acts as the reentrancy guard flag. When a facet is deployed, this `uint256` storage slot should be initialized to `0` (its default state) to indicate that reentrancy is not currently active. The `enter` function will revert if the flag is already set (indicating reentrancy), and `exit` will reset the flag. Ensure the `uint256` variable used for the lock is declared in the facet and is not used for other purposes.
- + diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx index eae3deae..6e81db5f 100644 --- a/website/docs/library/utils/index.mdx +++ b/website/docs/library/utils/index.mdx @@ -14,7 +14,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" From 9657916dc024b19e7bca7bdd9f906d019fbdd6e4 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 17:26:18 -0500 Subject: [PATCH 55/68] remove library page into library cat --- .../scripts/generate-docs-utils/category/index-page-generator.js | 1 + .github/scripts/generate-docs-utils/index-page-generator.js | 1 + website/docs/library/index.mdx | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/scripts/generate-docs-utils/category/index-page-generator.js b/.github/scripts/generate-docs-utils/category/index-page-generator.js index cfa401e0..9b625aac 100644 --- a/.github/scripts/generate-docs-utils/category/index-page-generator.js +++ b/.github/scripts/generate-docs-utils/category/index-page-generator.js @@ -116,6 +116,7 @@ function generateIndexMdxContent(label, description, items) { let mdxContent = `--- title: "${escapedLabel}" description: "${escapedDescription}" +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/.github/scripts/generate-docs-utils/index-page-generator.js b/.github/scripts/generate-docs-utils/index-page-generator.js index cfa401e0..9b625aac 100644 --- a/.github/scripts/generate-docs-utils/index-page-generator.js +++ b/.github/scripts/generate-docs-utils/index-page-generator.js @@ -116,6 +116,7 @@ function generateIndexMdxContent(label, description, items) { let mdxContent = `--- title: "${escapedLabel}" description: "${escapedDescription}" +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx index 7e19001e..8de81297 100644 --- a/website/docs/library/index.mdx +++ b/website/docs/library/index.mdx @@ -1,6 +1,7 @@ --- title: "Library" description: "API reference for all Compose modules and facets." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; From 48e00c6b82c423ef1e2996c3ac3fdc3618091316 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 17:27:08 -0500 Subject: [PATCH 56/68] change API to contract --- website/docs/library/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx index 8de81297..707c45f5 100644 --- a/website/docs/library/index.mdx +++ b/website/docs/library/index.mdx @@ -9,7 +9,7 @@ import DocSubtitle from '@site/src/components/docs/DocSubtitle'; import Icon from '@site/src/components/ui/Icon'; - API reference for all Compose modules and facets. + Contract reference for all Compose modules and facets. From cf7b95f9e4cf8b77f6a63f205956290bec69c12b Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 17:32:12 -0500 Subject: [PATCH 57/68] adjust sidebar redirect for lib --- website/sidebars.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/sidebars.js b/website/sidebars.js index a9b1d7df..fb17ed80 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -62,6 +62,10 @@ const sidebars = { type: 'category', label: 'Library', collapsed: true, + link: { + type: 'doc', + id: 'library/index', + }, items: [ { type: 'autogenerated', From c85b8efc866b4a72151e4f517e608e80fb572ac4 Mon Sep 17 00:00:00 2001 From: MN Date: Sun, 21 Dec 2025 18:31:04 -0500 Subject: [PATCH 58/68] remove table columns gap --- .../api/PropertyTable/styles.module.css | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/website/src/components/api/PropertyTable/styles.module.css b/website/src/components/api/PropertyTable/styles.module.css index d6a75d41..ae5e7dcc 100644 --- a/website/src/components/api/PropertyTable/styles.module.css +++ b/website/src/components/api/PropertyTable/styles.module.css @@ -20,6 +20,7 @@ .tableWrapper { position: relative; width: 100%; + max-width: 100%; border: 1px solid var(--ifm-color-emphasis-200); border-radius: 0.5rem; background: var(--ifm-background-surface-color); @@ -38,6 +39,7 @@ -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: var(--ifm-color-emphasis-300) transparent; + max-width: 100%; } /* Custom Scrollbar */ @@ -69,9 +71,10 @@ /* Table */ .table { width: 100%; + max-width: 100%; border-collapse: separate; border-spacing: 0; - min-width: 640px; + table-layout: auto; } /* Table Header */ @@ -162,22 +165,26 @@ /* Column Styles */ .nameColumn { - width: 20%; - min-width: 180px; + width: auto; + min-width: 120px; + max-width: 25%; } .typeColumn { - width: 15%; - min-width: 140px; + width: auto; + min-width: 100px; + max-width: 20%; } .requiredColumn { - width: 12%; - min-width: 100px; + width: auto; + min-width: 80px; + max-width: 15%; } .descriptionColumn { - width: auto; + width: 1%; /* Small width forces expansion to fill remaining space in auto layout */ + min-width: 200px; } /* Name Cell */ @@ -272,6 +279,8 @@ .descriptionCell { line-height: 1.6; color: var(--ifm-color-emphasis-700); + width: 100%; /* Ensure cell expands to fill column width */ + min-width: 0; /* Allow shrinking if needed, but column width will enforce expansion */ } [data-theme='dark'] .descriptionCell { From 5e06458dc92e03cf2c2f91621962f9aa06486ce9 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 18:37:15 -0500 Subject: [PATCH 59/68] add state var value, fix code highligh in table --- .../doc-generation-utils.js | 16 ++++----- .../generate-docs-utils/forge-doc-parser.js | 21 ++++++++++++ .../generate-docs-utils/templates/helpers.js | 26 +++++++++++++++ .../templates/template-engine-handlebars.js | 7 ++++ .../templates/templates.js | 33 +++++++++++++++---- 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/.github/scripts/generate-docs-utils/doc-generation-utils.js b/.github/scripts/generate-docs-utils/doc-generation-utils.js index 9c2cf42e..9155ecbd 100644 --- a/.github/scripts/generate-docs-utils/doc-generation-utils.js +++ b/.github/scripts/generate-docs-utils/doc-generation-utils.js @@ -112,23 +112,21 @@ function findForgeDocFiles(solFilePath) { /** * Determine if a contract is an interface * Interfaces should be skipped from documentation generation + * Only checks the naming pattern (I[A-Z]) to avoid false positives * @param {string} title - Contract title/name - * @param {string} content - File content (forge doc markdown) + * @param {string} content - File content (forge doc markdown) - unused but kept for API compatibility * @returns {boolean} True if this is an interface */ function isInterface(title, content) { - // Check if title follows interface naming convention: starts with "I" followed by uppercase + // Only check if title follows interface naming convention: starts with "I" followed by uppercase + // This is the most reliable indicator and avoids false positives from content that mentions "interface" if (title && /^I[A-Z]/.test(title)) { return true; } - // Check if content indicates it's an interface - if (content) { - const firstLines = content.split('\n').slice(0, 20).join('\n').toLowerCase(); - if (firstLines.includes('interface ') || firstLines.includes('*interface*')) { - return true; - } - } + // Removed content-based check to avoid false positives + // Facets and contracts often mention "interface" in their descriptions + // (e.g., "ERC-165 Standard Interface Detection Facet") which would incorrectly filter them return false; } diff --git a/.github/scripts/generate-docs-utils/forge-doc-parser.js b/.github/scripts/generate-docs-utils/forge-doc-parser.js index 3d1cc043..b49ce564 100644 --- a/.github/scripts/generate-docs-utils/forge-doc-parser.js +++ b/.github/scripts/generate-docs-utils/forge-doc-parser.js @@ -129,6 +129,27 @@ function parseForgeDocMarkdown(content, filePath) { } } else if (currentSection === 'structs') { currentItem.definition = codeContent; + } else if (currentSection === 'stateVariables') { + // Extract type and value from constant definition + // Format: "bytes32 constant NAME = value;" or "bytes32 NAME = value;" + // Handle both with and without "constant" keyword + // Note: name is already known from the ### heading, so we just need type and value + const constantMatch = codeContent.match(/(\w+(?:\s*\d+)?)\s+(?:constant\s+)?\w+\s*=\s*(.+?)(?:\s*;)?$/); + if (constantMatch) { + currentItem.type = constantMatch[1]; + currentItem.value = constantMatch[2].trim(); + } else { + // Fallback: try to extract just the value part if it's a simple assignment + const simpleMatch = codeContent.match(/=\s*(.+?)(?:\s*;)?$/); + if (simpleMatch) { + currentItem.value = simpleMatch[1].trim(); + } + // Try to extract type from the beginning + const typeMatch = codeContent.match(/^(\w+(?:\s*\d+)?)\s+/); + if (typeMatch) { + currentItem.type = typeMatch[1]; + } + } } continue; } diff --git a/.github/scripts/generate-docs-utils/templates/helpers.js b/.github/scripts/generate-docs-utils/templates/helpers.js index ad66992f..3226b690 100644 --- a/.github/scripts/generate-docs-utils/templates/helpers.js +++ b/.github/scripts/generate-docs-utils/templates/helpers.js @@ -73,6 +73,7 @@ function escapeJsx(str) { .replace(/\n/g, ' ') .replace(/\{/g, '{') .replace(/\}/g, '}') + // Don't escape backticks - they should be preserved for code formatting .trim(); } @@ -108,6 +109,7 @@ function escapeHtml(str) { /** * Escape string for use in JavaScript/JSX object literal values * Escapes quotes and backslashes for JavaScript strings (not HTML entities) + * Preserves backticks for code formatting * @param {string} str - String to escape * @returns {string} Escaped string safe for JavaScript string literals */ @@ -120,11 +122,35 @@ function escapeJsString(str) { .replace(/\n/g, '\\n') // Escape newlines .replace(/\r/g, '\\r') // Escape carriage returns .replace(/\t/g, '\\t'); // Escape tabs + // Note: Backticks are preserved for code formatting in descriptions +} + +/** + * Escape string for JSX string attributes, preserving backticks for code formatting + * This is specifically for descriptions that may contain code with backticks + * @param {string} str - String to escape + * @returns {string} Escaped string safe for JSX string attributes with preserved backticks + */ +function escapeJsxPreserveBackticks(str) { + if (!str) return ''; + + // Don't use sanitizeForMdx as it might HTML-escape things + // Just escape what's needed for JSX string attributes + return String(str) + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/"/g, '\\"') // Escape double quotes for JSX strings + .replace(/'/g, "\\'") // Escape single quotes + .replace(/\n/g, ' ') // Replace newlines with spaces + .replace(/\{/g, '{') // Escape curly braces for JSX + .replace(/\}/g, '}') // Escape curly braces for JSX + // Preserve backticks - don't escape them, they're needed for code formatting + .trim(); } module.exports = { escapeYaml, escapeJsx, + escapeJsxPreserveBackticks, sanitizeForMdx, sanitizeMdx: sanitizeForMdx, // Alias for template usage toJsxExpression, diff --git a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js index b852dd91..18fb38d0 100644 --- a/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js +++ b/.github/scripts/generate-docs-utils/templates/template-engine-handlebars.js @@ -23,6 +23,13 @@ function registerHelpers() { // Register escape helpers Handlebars.registerHelper('escapeYaml', helpers.escapeYaml); Handlebars.registerHelper('escapeJsx', helpers.escapeJsx); + // Helper to escape JSX strings while preserving backticks for code formatting + Handlebars.registerHelper('escapeJsxPreserveBackticks', function(value) { + if (!value) return ''; + const escaped = helpers.escapeJsxPreserveBackticks(value); + // Return as SafeString to prevent Handlebars from HTML-escaping backticks + return new Handlebars.SafeString(escaped); + }); Handlebars.registerHelper('sanitizeMdx', helpers.sanitizeMdx); Handlebars.registerHelper('escapeMarkdownTable', helpers.escapeMarkdownTable); // Helper to escape value for JavaScript strings in JSX object literals diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index 9931f20c..a2243269 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -560,12 +560,33 @@ function prepareBaseData(data, position = 99) { hasStructs: (data.structs || []).length > 0, // State variables (for modules) - with fallback description generation - stateVariables: (data.stateVariables || []).map(v => ({ - name: v.name, - type: v.type || '', - value: v.value || '', - description: v.description || generateStateVariableDescription(v.name, data.title), - })), + stateVariables: (data.stateVariables || []).map(v => { + const baseDescription = v.description || generateStateVariableDescription(v.name, data.title); + let description = baseDescription; + + // Append value to description if it exists and isn't already included + if (v.value && v.value.trim()) { + const valueStr = v.value.trim(); + // Check if value is already in description (case-insensitive) + // Escape special regex characters in valueStr + const escapedValue = valueStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + // Pattern matches "(Value: `...`)" or "(Value: ...)" format + const valuePattern = new RegExp('\\(Value:\\s*[`]?[^`)]*' + escapedValue + '[^`)]*[`]?\\)', 'i'); + if (!valuePattern.test(description)) { + // Format the value for display with backticks + // Use string concatenation to avoid template literal backtick issues + const valuePart = '(Value: `' + valueStr + '`)'; + description = baseDescription ? baseDescription + ' ' + valuePart : valuePart; + } + } + + return { + name: v.name, + type: v.type || '', + value: v.value || '', + description: description, + }; + }), hasStateVariables: (data.stateVariables || []).length > 0, hasStorage: Boolean(data.storageInfo || (data.stateVariables && data.stateVariables.length > 0)), }; From 2d9c1bbd6ba013311d202cfc0692da1fe908e33e Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 18:37:34 -0500 Subject: [PATCH 60/68] update contract template --- .../templates/pages/contract.mdx.template | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template index b414aa2f..1723cadf 100644 --- a/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template +++ b/.github/scripts/generate-docs-utils/templates/pages/contract.mdx.template @@ -78,7 +78,7 @@ This module provides internal functions for use in your custom facets. Import it { name: "{{name}}", type: "{{#if type}}{{type}}{{else}}constant{{/if}}", - description: "{{#if description}}{{escapeJsx description}}{{/if}}{{#if value}} (Value: `{{escapeJsString value}}`){{/if}}" + description: "{{#if description}}{{escapeJsxPreserveBackticks description}}{{/if}}" }{{#unless @last}},{{/unless}} {{/each}} ]} @@ -112,7 +112,7 @@ This module provides internal functions for use in your custom facets. Import it { name: "{{name}}", type: "{{type}}", - description: "{{#if description}}{{escapeJsx description}}{{/if}}" + description: "{{#if description}}{{escapeJsxPreserveBackticks description}}{{/if}}" }{{#unless @last}},{{/unless}} {{/each}} ]} @@ -129,7 +129,7 @@ This module provides internal functions for use in your custom facets. Import it { name: "{{name}}", type: "{{type}}", - description: "{{#if description}}{{escapeJsx description}}{{/if}}" + description: "{{#if description}}{{escapeJsxPreserveBackticks description}}{{/if}}" }{{#unless @last}},{{/unless}} {{/each}} ]} @@ -173,7 +173,7 @@ This module provides internal functions for use in your custom facets. Import it { name: "{{name}}", type: "{{type}}", - description: "{{#if description}}{{escapeJsx description}}{{/if}}" + description: "{{#if description}}{{escapeJsxPreserveBackticks description}}{{/if}}" }{{#unless @last}},{{/unless}} {{/each}} ]} From eb64451902b033f1d47c12b03abfa51929c6a122 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 18:38:11 -0500 Subject: [PATCH 61/68] improve table style --- .../src/components/api/PropertyTable/index.js | 28 ++++++++++++++++++- .../api/PropertyTable/styles.module.css | 21 ++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/website/src/components/api/PropertyTable/index.js b/website/src/components/api/PropertyTable/index.js index 496f2fc3..22fd68e0 100644 --- a/website/src/components/api/PropertyTable/index.js +++ b/website/src/components/api/PropertyTable/index.js @@ -1,6 +1,32 @@ import React from 'react'; import styles from './styles.module.css'; +/** + * Parse description string and convert markdown-style code (backticks) to JSX code elements + * @param {string|React.ReactNode} description - Description string or React element + * @returns {React.ReactNode} Description with code elements rendered + */ +function parseDescription(description) { + if (!description || typeof description !== 'string') { + return description; + } + + // Split by backticks and alternate between text and code + const parts = description.split(/(`[^`]+`)/g); + return parts.map((part, index) => { + if (part.startsWith('`') && part.endsWith('`')) { + // This is a code block + const codeContent = part.slice(1, -1); // Remove backticks + return ( + + {codeContent} + + ); + } + return {part}; + }); +} + /** * PropertyTable Component - Modern API property documentation table * Inspired by Shadcn UI design patterns @@ -51,7 +77,7 @@ export default function PropertyTable({ )} - {prop.description || prop.desc || '-'} + {prop.descriptionElement || parseDescription(prop.description || prop.desc) || '-'} {prop.default !== undefined && (
Default: {String(prop.default)} diff --git a/website/src/components/api/PropertyTable/styles.module.css b/website/src/components/api/PropertyTable/styles.module.css index ae5e7dcc..c50a2be5 100644 --- a/website/src/components/api/PropertyTable/styles.module.css +++ b/website/src/components/api/PropertyTable/styles.module.css @@ -319,6 +319,27 @@ color: #93c5fd; } +/* Inline code in descriptions */ +.descriptionCell .inlineCode { + font-family: var(--ifm-font-family-monospace); + font-size: 0.8125rem; + font-weight: 500; + background: var(--ifm-color-emphasis-100); + padding: 0.25rem 0.5rem; + border-radius: 0.375rem; + color: var(--ifm-color-primary); + border: 1px solid var(--ifm-color-emphasis-200); + display: inline-block; + line-height: 1.4; + margin: 0 0.125rem; +} + +[data-theme='dark'] .descriptionCell .inlineCode { + background: rgba(59, 130, 246, 0.1); + border-color: rgba(59, 130, 246, 0.2); + color: #93c5fd; +} + /* Responsive Design */ @media (max-width: 996px) { .propertyTable { From a26a353cac4c3e4b8bae2eca4ed68c9d0c75a4e5 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 18:46:48 -0500 Subject: [PATCH 62/68] remove internal function in facet doc --- .github/scripts/generate-docs-utils/config.js | 4 ++-- .../generate-docs-utils/forge-doc-parser.js | 21 ------------------ .../templates/templates.js | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index a8e9327a..814b9f88 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -21,10 +21,10 @@ module.exports = { // ============================================================================ /** - * Base output directory for library documentation + * Base output directory for contract documentation * Structure mirrors src/ automatically */ - libraryOutputDir: 'website/docs/library', + contractsOutputDir: 'website/docs/contracts', // ============================================================================ // Sidebar Positions diff --git a/.github/scripts/generate-docs-utils/forge-doc-parser.js b/.github/scripts/generate-docs-utils/forge-doc-parser.js index b49ce564..3d1cc043 100644 --- a/.github/scripts/generate-docs-utils/forge-doc-parser.js +++ b/.github/scripts/generate-docs-utils/forge-doc-parser.js @@ -129,27 +129,6 @@ function parseForgeDocMarkdown(content, filePath) { } } else if (currentSection === 'structs') { currentItem.definition = codeContent; - } else if (currentSection === 'stateVariables') { - // Extract type and value from constant definition - // Format: "bytes32 constant NAME = value;" or "bytes32 NAME = value;" - // Handle both with and without "constant" keyword - // Note: name is already known from the ### heading, so we just need type and value - const constantMatch = codeContent.match(/(\w+(?:\s*\d+)?)\s+(?:constant\s+)?\w+\s*=\s*(.+?)(?:\s*;)?$/); - if (constantMatch) { - currentItem.type = constantMatch[1]; - currentItem.value = constantMatch[2].trim(); - } else { - // Fallback: try to extract just the value part if it's a simple assignment - const simpleMatch = codeContent.match(/=\s*(.+?)(?:\s*;)?$/); - if (simpleMatch) { - currentItem.value = simpleMatch[1].trim(); - } - // Try to extract type from the beginning - const typeMatch = codeContent.match(/^(\w+(?:\s*\d+)?)\s+/); - if (typeMatch) { - currentItem.type = typeMatch[1]; - } - } } continue; } diff --git a/.github/scripts/generate-docs-utils/templates/templates.js b/.github/scripts/generate-docs-utils/templates/templates.js index a2243269..79c07b14 100644 --- a/.github/scripts/generate-docs-utils/templates/templates.js +++ b/.github/scripts/generate-docs-utils/templates/templates.js @@ -251,6 +251,20 @@ function filterAndNormalizeParams(params, functionName) { })); } +/** + * Check if a function is internal by examining its signature + * @param {object} fn - Function data with signature property + * @returns {boolean} True if function is internal + */ +function isInternalFunction(fn) { + if (!fn || !fn.signature) return false; + + // Check if signature contains "internal" as a whole word + // Use word boundary regex to avoid matching "internalTransferFrom" etc. + const internalPattern = /\binternal\b/; + return internalPattern.test(fn.signature); +} + /** * Prepare function data for template rendering (shared between facet and module) * @param {object} fn - Function data @@ -602,6 +616,9 @@ function prepareFacetData(data, position = 99) { const baseData = prepareBaseData(data, position); const sourceFilePath = data.sourceFilePath; + // Filter out internal functions for facets (they act as pre-deploy logic blocks) + const publicFunctions = (data.functions || []).filter(fn => !isInternalFunction(fn)); + return { ...baseData, // Contract type flags for unified template @@ -609,8 +626,9 @@ function prepareFacetData(data, position = 99) { isModule: false, contractType: 'facet', // Functions with APIReference-compatible format (no source extraction for facets) - functions: (data.functions || []).map(fn => prepareFunctionData(fn, sourceFilePath, false)), - hasFunctions: (data.functions || []).length > 0, + // Only include non-internal functions since facets are pre-deploy logic blocks + functions: publicFunctions.map(fn => prepareFunctionData(fn, sourceFilePath, false)), + hasFunctions: publicFunctions.length > 0, }; } From 5a95f564c14c0647b79d618d8b1415c0f41ea7a2 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 18:50:32 -0500 Subject: [PATCH 63/68] normalize source path to always refer to Perfect-Abstractions/Compose repo --- .github/scripts/generate-docs-utils/config.js | 42 +++++++++++++++++++ .../generate-docs-utils/forge-doc-parser.js | 8 ++-- .github/scripts/generate-docs.js | 8 +++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/.github/scripts/generate-docs-utils/config.js b/.github/scripts/generate-docs-utils/config.js index 814b9f88..e436c41a 100644 --- a/.github/scripts/generate-docs-utils/config.js +++ b/.github/scripts/generate-docs-utils/config.js @@ -108,4 +108,46 @@ module.exports = { NonReentrancyMod: 1, ERC165Mod: 1, }, + + // ============================================================================ + // Repository Configuration + // ============================================================================ + + /** Main repository URL - always use this for source links */ + mainRepoUrl: 'https://github.com/Perfect-Abstractions/Compose', + + /** + * Normalize gitSource URL to always point to the main repository's main branch + * Replaces any fork or incorrect repository URLs with the main repo URL + * Converts blob URLs to tree URLs pointing to main branch + * @param {string} gitSource - Original gitSource URL from forge doc + * @returns {string} Normalized gitSource URL + */ + normalizeGitSource(gitSource) { + if (!gitSource) return gitSource; + + // Pattern: https://github.com/USER/Compose/blob/COMMIT/src/path/to/file.sol + // Convert to: https://github.com/Perfect-Abstractions/Compose/tree/main/src/path/to/file.sol + const githubUrlPattern = /https:\/\/github\.com\/[^\/]+\/Compose\/(?:blob|tree)\/[^\/]+\/(.+)/; + const match = gitSource.match(githubUrlPattern); + + if (match) { + // Extract the path after the repo name (should start with src/) + const pathPart = match[1]; + // Ensure it starts with src/ (remove any leading src/ if duplicated) + const normalizedPath = pathPart.startsWith('src/') ? pathPart : `src/${pathPart}`; + return `${this.mainRepoUrl}/tree/main/${normalizedPath}`; + } + + // If it doesn't match the pattern, try to construct from the main repo + // Extract just the file path if it's a relative path or partial URL + if (gitSource.includes('/src/')) { + const srcIndex = gitSource.indexOf('/src/'); + const pathAfterSrc = gitSource.substring(srcIndex + 1); + return `${this.mainRepoUrl}/tree/main/${pathAfterSrc}`; + } + + // If it doesn't match any pattern, return as-is (might be a different format) + return gitSource; + }, }; diff --git a/.github/scripts/generate-docs-utils/forge-doc-parser.js b/.github/scripts/generate-docs-utils/forge-doc-parser.js index 3d1cc043..d856b8ef 100644 --- a/.github/scripts/generate-docs-utils/forge-doc-parser.js +++ b/.github/scripts/generate-docs-utils/forge-doc-parser.js @@ -3,6 +3,8 @@ * Extracts structured data from forge-generated markdown files */ +const config = require('./config'); + /** * Parse forge doc markdown output into structured data * @param {string} content - Markdown content from forge doc @@ -44,7 +46,7 @@ function parseForgeDocMarkdown(content, filePath) { if (trimmedLine.startsWith('[Git Source]')) { const match = trimmedLine.match(/\[Git Source\]\((.*?)\)/); if (match) { - data.gitSource = match[1]; + data.gitSource = config.normalizeGitSource(match[1]); } continue; } @@ -422,7 +424,7 @@ function parseIndividualItemFile(content, filePath) { if (trimmedLine.startsWith('[Git Source]')) { const match = trimmedLine.match(/\[Git Source\]\((.*?)\)/); if (match) { - gitSource = match[1]; + gitSource = config.normalizeGitSource(match[1]); } continue; } @@ -668,7 +670,7 @@ function aggregateParsedItems(parsedItems, sourceFilePath) { // Extract git source from first item for (const parsed of parsedItems) { if (parsed && parsed.gitSource) { - data.gitSource = parsed.gitSource; + data.gitSource = config.normalizeGitSource(parsed.gitSource); break; } } diff --git a/.github/scripts/generate-docs.js b/.github/scripts/generate-docs.js index 63c6890f..b9970e3c 100644 --- a/.github/scripts/generate-docs.js +++ b/.github/scripts/generate-docs.js @@ -39,6 +39,7 @@ const { const { generateFacetDoc, generateModuleDoc } = require('./generate-docs-utils/templates/templates'); const { enhanceWithAI, shouldSkipEnhancement, addFallbackContent } = require('./generate-docs-utils/ai-enhancement'); const { syncDocsStructure, regenerateAllIndexFiles } = require('./generate-docs-utils/category-generator'); +const config = require('./generate-docs-utils/config'); // ============================================================================ // Tracking @@ -251,7 +252,12 @@ async function processAggregatedFiles(forgeDocFiles, solFilePath) { } if (gitSource) { - data.gitSource = gitSource; + data.gitSource = config.normalizeGitSource(gitSource); + } + + // Also normalize gitSource from aggregated data if present + if (data.gitSource) { + data.gitSource = config.normalizeGitSource(data.gitSource); } const contractType = getContractType(solFilePath, ''); From a365d4e48a2dbf0e35121239ab6b1bf62e38cff5 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Tue, 23 Dec 2025 00:04:15 +0000 Subject: [PATCH 64/68] docs: auto-generate docs pages from NatSpec --- .../AccessControl/AccessControlFacet.mdx | 98 +++------- .../access/AccessControl/AccessControlMod.mdx | 62 ++++--- .../library/access/AccessControl/index.mdx | 5 +- .../AccessControlPausableFacet.mdx | 109 ++++------- .../AccessControlPausableMod.mdx | 48 ++--- .../access/AccessControlPausable/index.mdx | 5 +- .../AccessControlTemporalFacet.mdx | 108 +++-------- .../AccessControlTemporalMod.mdx | 58 +++--- .../access/AccessControlTemporal/index.mdx | 5 +- .../docs/library/access/Owner/OwnerFacet.mdx | 60 ++---- .../docs/library/access/Owner/OwnerMod.mdx | 55 +++--- website/docs/library/access/Owner/index.mdx | 5 +- .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 86 +++------ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 55 +++--- .../library/access/OwnerTwoSteps/index.mdx | 3 +- website/docs/library/access/index.mdx | 1 + .../docs/library/diamond/DiamondCutFacet.mdx | 175 +++++------------- .../docs/library/diamond/DiamondCutMod.mdx | 49 ++--- .../library/diamond/DiamondInspectFacet.mdx | 65 ++----- .../library/diamond/DiamondLoupeFacet.mdx | 57 +++--- website/docs/library/diamond/DiamondMod.mdx | 36 ++-- .../diamond/example/ExampleDiamond.mdx | 52 +++--- .../docs/library/diamond/example/index.mdx | 3 +- website/docs/library/diamond/index.mdx | 11 +- website/docs/library/index.mdx | 2 +- .../interfaceDetection/ERC165/ERC165Facet.mdx | 140 ++++++++++++++ .../interfaceDetection/ERC165/ERC165Mod.mdx | 36 ++-- .../interfaceDetection/ERC165/index.mdx | 10 +- .../docs/library/interfaceDetection/index.mdx | 1 + .../library/token/ERC1155/ERC1155Facet.mdx | 69 +++---- .../docs/library/token/ERC1155/ERC1155Mod.mdx | 42 ++--- website/docs/library/token/ERC1155/index.mdx | 5 +- .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 70 ++----- .../library/token/ERC20/ERC20/ERC20Facet.mdx | 61 ++---- .../library/token/ERC20/ERC20/ERC20Mod.mdx | 60 +++--- .../docs/library/token/ERC20/ERC20/index.mdx | 7 +- .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 79 +++----- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 59 +++--- .../token/ERC20/ERC20Bridgeable/index.mdx | 5 +- .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 94 ++++------ .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 48 ++--- .../library/token/ERC20/ERC20Permit/index.mdx | 5 +- website/docs/library/token/ERC20/index.mdx | 1 + .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 72 +++---- .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 73 ++++---- .../library/token/ERC6909/ERC6909/index.mdx | 5 +- website/docs/library/token/ERC6909/index.mdx | 1 + .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 63 ++----- .../token/ERC721/ERC721/ERC721Facet.mdx | 88 ++------- .../library/token/ERC721/ERC721/ERC721Mod.mdx | 48 ++--- .../library/token/ERC721/ERC721/index.mdx | 3 +- .../ERC721EnumerableBurnFacet.mdx | 65 +++---- .../ERC721EnumerableFacet.mdx | 103 +++-------- .../ERC721Enumerable/ERC721EnumerableMod.mdx | 60 +++--- .../token/ERC721/ERC721Enumerable/index.mdx | 7 +- website/docs/library/token/ERC721/index.mdx | 1 + .../library/token/Royalty/RoyaltyFacet.mdx | 57 ++---- .../docs/library/token/Royalty/RoyaltyMod.mdx | 55 +++--- website/docs/library/token/Royalty/index.mdx | 5 +- website/docs/library/token/index.mdx | 1 + .../docs/library/utils/NonReentrancyMod.mdx | 56 +++--- website/docs/library/utils/index.mdx | 3 +- 62 files changed, 1162 insertions(+), 1609 deletions(-) create mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx index 8c6166a4..06c9edde 100644 --- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlFacet" -description: "Manage roles and permissions within the diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControl/AccessControlFacet.sol" +description: "Manages role-based access control within a Compose diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage roles and permissions within the diamond. +Manages role-based access control within a Compose diamond. -- Role-based access control system. -- Supports granting and revoking roles for individual accounts or batches. -- Provides functions to check role membership and enforce role requirements. +- Hierarchical role administration: Roles can have their own designated admin roles. +- Batch operations for granting and revoking roles efficiently. +- Explicit error types for unauthorized access attempts. ## Overview -The AccessControlFacet provides a robust role-based access control system for your Compose diamond. It allows for granular permission management, enabling you to define administrative roles and grant specific privileges to accounts. This facet ensures that sensitive operations are performed only by authorized entities, enhancing the security and integrity of your diamond. +This facet provides a robust role-based access control (RBAC) system, enabling granular permission management for any Compose diamond. It allows defining roles, assigning them to accounts, and enforcing role requirements on function calls, ensuring that only authorized entities can perform sensitive operations. --- @@ -67,28 +67,6 @@ The AccessControlFacet provides a robust role-based access control system for yo ## Functions -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- ### hasRole Returns if an account has a role. @@ -499,62 +477,44 @@ error AccessControlUnauthorizedSender(address _sender, address _account); {`pragma solidity ^0.8.30; -import {DiamondCutFacet} from "@compose/diamond-cut/src/DiamondCutFacet.sol"; -import {AccessControlFacet} from "@compose/access-control/src/AccessControlFacet.sol"; - -contract MyDiamond is DiamondInit { - - function upgrade() public { - DiamondCutFacet diamondCutFacet = DiamondCutFacet(address(this)); - AccessControlFacet accessControlFacet = new AccessControlFacet(); - - diamondCutFacet.diamondCut([ - FacetCut({ - facetAddress: address(accessControlFacet), - action: FacetCutAction.ADD, - functionSelectors: - AccessControlFacet.getStorage.selector - | AccessControlFacet.hasRole.selector - | AccessControlFacet.requireRole.selector - | AccessControlFacet.getRoleAdmin.selector - | AccessControlFacet.setRoleAdmin.selector - | AccessControlFacet.grantRole.selector - | AccessControlFacet.revokeRole.selector - | AccessControlFacet.grantRoleBatch.selector - | AccessControlFacet.revokeRoleBatch.selector - | AccessControlFacet.renounceRole.selector - }) - ], address(0), ""); - - // Initialize Access Control - address accessControlAddress = diamondCutFacet.getFacetAddress(AccessControlFacet.getStorage.selector); - AccessControlFacet(accessControlAddress).grantRole(AccessControlFacet.DEFAULT_ADMIN_ROLE(), msg.sender); +import {AccessControlFacet} from "@compose/access-control/AccessControlFacet.sol"; +import {DiamondProxy} from "@compose/diamond/DiamondProxy.sol"; + +contract MyDiamond is DiamondProxy { + constructor(address _diamondAdmin, address[] memory _initFacets) DiamondProxy(_diamondAdmin, _initFacets) {} + + function grantAdminRole() external { + // Example: Granting the DEFAULT_ADMIN_ROLE to the contract deployer + address deployer = msg.sender; + AccessControlFacet acFacet = AccessControlFacet(address(this)); + bytes32 adminRole = acFacet.getRoleAdmin(AccessControlFacet.DEFAULT_ADMIN_ROLE); + acFacet.grantRole(adminRole, deployer); } - function checkPermission() public view { - address accessControlAddress = diamondCutFacet.getFacetAddress(AccessControlFacet.getStorage.selector); - AccessControlFacet(accessControlAddress).requireRole(AccessControlFacet.DEFAULT_ADMIN_ROLE(), msg.sender); - // ... perform privileged operation ... + function hasAdminRole(address _account) external view returns (bool) { + AccessControlFacet acFacet = AccessControlFacet(address(this)); + return acFacet.hasRole(AccessControlFacet.DEFAULT_ADMIN_ROLE, _account); } -}`} +} +`} ## Best Practices -- Grant the `DEFAULT_ADMIN_ROLE` only to trusted deployer or initial admin addresses. -- Use `grantRoleBatch` and `revokeRoleBatch` for efficiency when managing multiple accounts for a single role. -- Regularly audit role assignments to ensure adherence to the principle of least privilege. +- Initialize roles and grant administrative permissions during diamond deployment or upgrade to establish a secure baseline. +- Use `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple role assignments or revocations. +- Integrate role checks directly into facet functions using `requireRole` to enforce access control at the point of execution. ## Security Considerations -Ensure that the `DEFAULT_ADMIN_ROLE` is securely managed, as it has the power to grant or revoke any role. Reentrancy is not a concern as most functions perform state changes and then emit events without external calls. Input validation is handled internally by the facet to prevent invalid role or account assignments. +Ensure that the initial administrative roles are set correctly during deployment. Be mindful of the potential for denial-of-service if all roles are revoked from critical accounts. Access control checks are enforced by the facet itself; ensure functions requiring specific permissions correctly utilize `requireRole` or equivalent checks.
- + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx index 497cae93..78ce5b31 100644 --- a/website/docs/library/access/AccessControl/AccessControlMod.mdx +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlMod" -description: "Manage role-based access control within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControl/AccessControlMod.sol" +description: "Manages role-based access control within a diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage role-based access control within a diamond. +Manages role-based access control within a diamond. - Role-based access control for granular permission management. -- Functions for granting, revoking, and checking role assignments. -- Ability to define and manage administrative roles for other roles. +- Functions to grant, revoke, and check roles for any account. +- Ability to set administrative roles for other roles, enabling hierarchical control. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides a robust framework for implementing role-based access control (RBAC) within Compose diamonds. It allows for granular permission management by assigning roles to accounts, ensuring that only authorized users can execute specific functions. By adhering to Compose's storage pattern, it integrates seamlessly with diamond upgrades. +The AccessControl module provides a robust system for managing roles and permissions within your Compose diamond. It allows for granular control over which accounts can perform specific actions by assigning them roles. This module is essential for building secure and auditable decentralized applications, ensuring that only authorized entities can execute sensitive functions. --- @@ -401,51 +401,53 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlMod} from "@compose/modules/access-control/IAccessControlMod.sol"; +import {IAccessControlMod} from "@compose-protocol/diamond-contracts/contracts/modules/access-control/AccessControlMod.sol"; contract MyFacet { - IAccessControlMod internal accessControlMod; + IAccessControlMod internal immutable accessControl; - constructor(address _accessControlModAddress) { - accessControlMod = IAccessControlMod(_accessControlModAddress); + constructor(address _diamondAddress) { + accessControl = IAccessControlMod(_diamondAddress); } - function someRestrictedFunction() external { - address sender = msg.sender; - bytes32 ownerRole = keccak256("OWNER_ROLE"); - - accessControlMod.requireRole(ownerRole, sender); - - // ... execute restricted logic ... + /** + * @notice Grants the DEFAULT_ADMIN_ROLE to the caller. + */ + function grantDefaultAdmin() external { + address caller = msg.sender; + bytes32 adminRole = accessControl.DEFAULT_ADMIN_ROLE(); // Assuming DEFAULT_ADMIN_ROLE is accessible or defined + accessControl.grantRole(adminRole, caller); } - function grantOwnerRole(address _account) external { - address sender = msg.sender; - bytes32 ownerRole = keccak256("OWNER_ROLE"); - bytes32 adminRole = keccak256("DEFAULT_ADMIN_ROLE"); - - accessControlMod.requireRole(adminRole, sender); - accessControlMod.grantRole(ownerRole, _account); + /** + * @notice Checks if an account has a specific role. + * @param _role The role to check. + * @param _account The account to check. + * @return bool True if the account has the role, false otherwise. + */ + function checkRole(bytes32 _role, address _account) external view returns (bool) { + return accessControl.hasRole(_role, _account); } -}`} +} +`} ## Best Practices -- Use `requireRole` to enforce access control checks at the beginning of sensitive functions, reverting with `AccessControlUnauthorizedAccount` if the caller lacks the necessary role. -- Define roles using `keccak256` hashes for clarity and gas efficiency. -- Manage role administration carefully by setting appropriate `DEFAULT_ADMIN_ROLE` and using `setRoleAdmin` to control role hierarchy. +- Define custom roles and manage their administration carefully using `setRoleAdmin`. +- Use `requireRole` extensively to protect sensitive functions from unauthorized access. +- Ensure that the `DEFAULT_ADMIN_ROLE` is initially granted to a secure, multi-signature wallet or a trusted entity. ## Integration Notes -The `AccessControlMod` utilizes its own storage slot within the diamond's storage layout. Facets interact with this module through its interface (`IAccessControlMod`). Functions like `grantRole`, `revokeRole`, and `hasRole` directly read from and write to the module's storage. The `requireRole` function acts as a guard, reverting execution if the calling account does not possess the specified role. Ensure the `AccessControlMod` is initialized correctly during the diamond deployment process. +This module requires dedicated storage slots for its role mapping and role admin mapping. Facets can interact with this module by calling its external functions. The `hasRole` and `requireRole` functions provide immediate feedback on an account's permissions. Changes to role assignments or role administration are persistent and visible across all facets interacting with the diamond.
- + diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx index 3c1dc511..44eab9eb 100644 --- a/website/docs/library/access/AccessControl/index.mdx +++ b/website/docs/library/access/AccessControl/index.mdx @@ -1,6 +1,7 @@ --- title: "Access Control" description: "Role-based access control (RBAC) pattern." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx index c375d08a..62996189 100644 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlPausableFacet" -description: "Manage roles and pausing functionality within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +description: "Manage role pausing and access control within a diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage roles and pausing functionality within a diamond. +Manage role pausing and access control within a diamond. -- Role-specific pausing and unpausing capabilities. -- Integration with existing diamond access control mechanisms. -- Prevents execution of role-restricted functions when a role is paused. +- Allows temporary suspension of specific roles. +- Integrates seamlessly with existing Compose access control mechanisms. +- Provides explicit revert reasons for unauthorized access and paused roles. ## Overview -This facet provides granular control over role-based access and the ability to temporarily pause specific roles. It integrates with the diamond's access control system to enforce role restrictions and prevent execution when roles are paused, enhancing security and operational flexibility. +This facet provides granular control over role execution by allowing roles to be temporarily paused. It integrates with the diamond's access control system, ensuring that only authorized accounts can perform actions and that these actions can be suspended when a role is paused. --- @@ -76,50 +76,6 @@ This facet provides granular control over role-based access and the ability to t ## Functions -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- ### isRolePaused Returns if a role is paused. @@ -326,34 +282,31 @@ error AccessControlRolePaused(bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlPausableFacet} from "@compose/diamond/facets/AccessControlPausable/IAccessControlPausableFacet.sol"; +import {IDiamondCut, IDiamondLoupe} from "@compose/diamond-contracts/contracts/interfaces/IDiamond.sol"; -contract ExampleUsage { - IAccessControlPausableFacet accessControlPausableFacet; +import {AccessControlPausableFacet} from "@compose/diamond-contracts/contracts/facets/AccessControl/AccessControlPausableFacet.sol"; - constructor(address _diamondAddress) { - // Assume accessControlPausableFacet is a registered facet on the diamond - accessControlPausableFacet = IAccessControlPausableFacet(_diamondAddress); - } +contract DeployDiamond { + // ... deployment setup ... - function checkRolePaused(bytes32 _role) public view { - bool paused = accessControlPausableFacet.isRolePaused(_role); - // Use 'paused' variable - } + function deploy() public { + // ... deploy other facets ... - function pauseMyRole(bytes32 _role) public { - // Caller must be the admin of _role - accessControlPausableFacet.pauseRole(_role); - } + AccessControlPausableFacet accessControlPausableFacet = new AccessControlPausableFacet(); - function unpauseMyRole(bytes32 _role) public { - // Caller must be the admin of _role - accessControlPausableFacet.unpauseRole(_role); - } + // Add AccessControlPausableFacet to the diamond + // ... diamond cut call ... + + // Example: Pausing a role + bytes32 pauseRoleSelector = AccessControlPausableFacet.pauseRole.selector; + // Assuming 'adminAccount' is the caller and has the admin role for the target role + (bool success, ) = address(diamond).call(abi.encodeWithSelector(pauseRoleSelector, _roleToPause)); + require(success, "Failed to pause role"); - function ensureRoleActive(bytes32 _role) public { - // Reverts if caller is not authorized for _role or if _role is paused - accessControlPausableFacet.requireRoleNotPaused(_role); + // Example: Checking if a role is paused + bytes4 isRolePausedSelector = AccessControlPausableFacet.isRolePaused.selector; + (success, ) = address(diamond).call(abi.encodeWithSelector(isRolePausedSelector, _roleToCheck)); + // ... check result ... } }`} @@ -361,19 +314,19 @@ contract ExampleUsage { ## Best Practices -- Ensure the `AccessControlPausableFacet` is correctly initialized and its functions are accessible via the diamond proxy. -- Grant the `PAUSER_ROLE` and `ROLE_ADMIN_ROLE` (or equivalent roles defined by your access control implementation) judiciously, as they control pausing and unpausing operations. -- Use `requireRoleNotPaused` proactively within other facets to prevent execution when a role's functionality is temporarily suspended. +- Integrate this facet into your diamond to add role-specific pausing capabilities to your access control. +- Ensure the caller has the appropriate administrative role before attempting to pause or unpause a role. +- Use `requireRoleNotPaused` within your facet functions that are subject to role pausing to enforce the active state of a role. ## Security Considerations -Access to `pauseRole` and `unpauseRole` functions is restricted to the administrative role of the specific role being managed. Ensure that the administrative roles are themselves secured appropriately. Reentrancy is not a direct concern for `pauseRole` and `unpauseRole` as they only modify state, but calls within other facets that rely on `requireRoleNotPaused` should be audited for reentrancy risks. +Access to `pauseRole` and `unpauseRole` is restricted to the administrator of the specific role, preventing unauthorized pausing or unpausing. The `requireRoleNotPaused` function ensures that calls to role-protected functions will revert if the role is currently paused, preventing unintended execution.
- + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx index 9b4540eb..5a102bb0 100644 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlPausableMod" -description: "Manages role-based access control with pausing capabilities." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlPausable/AccessControlPausableMod.sol" +description: "Manage role-based access control with pause functionality." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages role-based access control with pausing capabilities. +Manage role-based access control with pause functionality. -- Role-specific pausing: Temporarily disable functionality tied to specific roles. -- Permission enforcement: `requireRoleNotPaused` checks both role membership and pause status. -- Diamond storage integration: Leverages the diamond's storage for persistent pause states. +- Role-specific pausing: Allows granular control over which roles can execute functions. +- Pause/unpause functionality: Enables temporary suspension and resumption of role-based operations. +- Integrated access control checks: `requireRoleNotPaused` verifies both role membership and pause status. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module enhances role-based access control by introducing the ability to pause specific roles. This allows for temporarily halting operations associated with a role without revoking permissions. It integrates seamlessly with the diamond's storage pattern, making paused states visible and manageable across facets. +This module extends role-based access control by introducing pause functionality for specific roles. It allows administrators to temporarily halt operations associated with a role, enhancing safety and control during critical operations or upgrades. This composable module integrates seamlessly into the Compose diamond storage pattern. --- @@ -330,22 +330,28 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlPausableMod} from "./interfaces/IAccessControlPausableMod.sol"; +import {IAccessControlPausableMod} from "@compose/modules/AccessControlPausableMod.sol"; contract MyFacet { - IAccessControlPausableMod public constant ACCESS_CONTROL_PAUSABLE_MOD = IAccessControlPausableMod(
); + IAccessControlPausableMod internal accessControlPausableMod; - function _someProtectedAction() internal view { - ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(msg.sender, IAccessControlBase.Role.OPERATOR); - // ... proceed with action + function initialize(address _accessControlPausableModAddress) external { + accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableModAddress); } - function _pauseOperatorRole() external { - ACCESS_CONTROL_PAUSABLE_MOD.pauseRole(IAccessControlBase.Role.OPERATOR); + function grantRoleAndPause(bytes32 _role) external { + // Assuming role granting is handled by another facet or module + // accessControlPausableMod.grantRole(_role, msg.sender); // Example, actual grant function may differ + accessControlPausableMod.pauseRole(_role); } - function _unpauseOperatorRole() external { - ACCESS_CONTROL_PAUSABLE_MOD.unpauseRole(IAccessControlBase.Role.OPERATOR); + function performSensitiveOperation(bytes32 _role) external { + accessControlPausableMod.requireRoleNotPaused(_role, msg.sender); + // Proceed with operation + } + + function unpauseSensitiveRole(bytes32 _role) external { + accessControlPausableMod.unpauseRole(_role); } }`} @@ -353,19 +359,19 @@ contract MyFacet { ## Best Practices -- Use `requireRoleNotPaused` to enforce role presence and ensure the role is not currently paused before executing sensitive actions. -- Implement pausing and unpausing logic carefully, ensuring only authorized entities can call these functions. -- Be aware that pausing a role affects all facets that rely on that role's active status. +- Ensure `requireRoleNotPaused` is called before critical operations to prevent execution when a role is paused. +- Implement role management (granting/revoking) in a separate, dedicated facet for clarity and separation of concerns. +- Use custom errors `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` for clear revert reasons. ## Integration Notes -This module interacts with the diamond's storage to track the paused status of roles. Facets can query `isRolePaused` or use `requireRoleNotPaused` to ensure operations are permitted. The `AccessControlPausableMod` contract is expected to reside at a known address within the diamond's facet registry. Changes to role pause states are immediately reflected across all interacting facets. +The `AccessControlPausableMod` module manages its state within its own storage slots, separate from other facets. Facets interacting with this module must obtain its address and call its functions. The `requireRoleNotPaused` function enforces invariants by reverting if the calling account lacks the specified role or if the role is currently paused. The module's storage is accessible via `getAccessControlStorage` and `getStorage` for auditing or debugging purposes, but direct modification is not recommended.
- + diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx index 9edc2a1e..0e056cb6 100644 --- a/website/docs/library/access/AccessControlPausable/index.mdx +++ b/website/docs/library/access/AccessControlPausable/index.mdx @@ -1,6 +1,7 @@ --- title: "Pausable Access Control" description: "RBAC with pause functionality." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx index c87ba94d..783d6256 100644 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +description: "Manages time-bound role assignments and checks for access control." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,19 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages time-bound role assignments within a Compose diamond. +Manages time-bound role assignments and checks for access control. -- Time-bound role assignments: Roles expire automatically after a specified timestamp. -- Admin-controlled granting and revoking: Only role administrators can manage temporal roles. -- Explicit expiry checks: Functions to check if a role has expired and to enforce valid, non-expired roles. +- Grants roles with specific expiration timestamps. +- Provides functions to check if a role assignment has expired. +- Enforces role validity, considering both existence and expiry. +- Role granting and revocation are restricted to the role's admin. ## Overview -This facet extends Compose's access control by introducing time-limited role assignments. It allows administrators to grant roles that automatically expire, enhancing granular control over permissions. The facet provides mechanisms to grant, revoke, and verify the validity of these time-bound roles. +This facet extends Compose's access control by introducing time-bound role assignments. It allows for granting roles that automatically expire and provides mechanisms to check for role validity, including expiry status. This enables dynamic access control policies based on time. --- @@ -76,50 +77,6 @@ This facet extends Compose's access control by introducing time-limited role ass ## Functions -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- ### getRoleExpiry Returns the expiry timestamp for a role assignment. @@ -193,7 +150,7 @@ Checks if a role assignment has expired. { name: "-", type: "bool", - description: "True if the role has expired or doesn\'t exist, false if still valid." + description: "True if the role has expired or doesn\'t exist, false if still valid." } ]} showRequired={false} @@ -403,34 +360,25 @@ error AccessControlRoleExpired(bytes32 _role, address _account); {`pragma solidity ^0.8.30; -import {IComposeDiamond} from "@compose-protocol/diamond-contracts/contracts/diamond/IComposeDiamond.sol"; -import {AccessControlTemporalFacet} from "@compose-protocol/diamond-contracts/contracts/facets/AccessControlTemporalFacet.sol"; +import {AccessControlTemporalFacet} from "@compose/access-control/facets/AccessControlTemporalFacet.sol"; -contract Deployer { - address public diamondAddress; +contract MyDiamond { + // Assume AccessControlFacet and diamond deploy logic are present - function deploy() public { - // Assume diamondAddress is already set and the facet is added - diamondAddress = address(0x123...); // Replace with actual diamond address - - IComposeDiamond diamond = IComposeDiamond(diamondAddress); + function grantRoleWithExpiry(bytes32 role, address account, uint64 expiry) external { + AccessControlTemporalFacet(diamondProxyAddress).grantRoleWithExpiry(role, account, expiry); + } - // Example: Grant 'PAUSER' role to address(0x456...) for 1 hour from now - uint256 expiryTimestamp = block.timestamp + 1 hours; - bytes32 PAUSER_ROLE = keccak256("PAUSER"); - diamond.callFacetFunction(AccessControlTemporalFacet.getFunctionSelector("grantRoleWithExpiry(bytes32,address,uint256)"), - abi.encodeCall(AccessControlTemporalFacet.grantRoleWithExpiry, - (PAUSER_ROLE, address(0x456...), expiryTimestamp) - ) - ); + function revokeTemporalRole(bytes32 role, address account) external { + AccessControlTemporalFacet(diamondProxyAddress).revokeTemporalRole(role, account); + } - // Example: Check if a role is expired - bool expired = AccessControlTemporalFacet(diamondAddress).isRoleExpired(PAUSER_ROLE, address(0x456...)); + function isRoleExpired(bytes32 role, address account) external view returns (bool) { + return AccessControlTemporalFacet(diamondProxyAddress).isRoleExpired(role, account); + } - // Example: Require a valid role (will revert if not valid or expired) - try AccessControlTemporalFacet(diamondAddress).requireValidRole(PAUSER_ROLE, address(0x456...)) { - // Role is valid and not expired - } catch AccessControlTemporalFacet.AccessControlRoleExpired {} catch AccessControlTemporalFacet.AccessControlUnauthorizedAccount {} + function requireValidRole(bytes32 role, address account) external view { + AccessControlTemporalFacet(diamondProxyAddress).requireValidRole(role, account); } }`} @@ -438,19 +386,19 @@ contract Deployer { ## Best Practices -- Grant roles with expiry only to trusted accounts and with appropriate lifespans. -- Regularly monitor role assignments to ensure they align with current operational needs. -- Utilize `requireValidRole` within your facet logic to enforce time-bound access control checks. +- Use `grantRoleWithExpiry` to assign roles with a predefined expiration. Ensure the caller has the necessary administrative privileges for the target role. +- Utilize `isRoleExpired` or `requireValidRole` to enforce time-sensitive access control checks within your application logic. +- Integrate this facet carefully to manage the lifecycle of roles that should not be permanent. ## Security Considerations -The `grantRoleWithExpiry` and `revokeTemporalRole` functions are restricted to the admin of the respective role, preventing unauthorized modifications. Ensure that the admin role itself is adequately secured. The `requireValidRole` function prevents the use of expired roles, mitigating risks associated with stale permissions. Reentrancy is not a concern as these functions do not make external calls. +Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the administrative role for the specified role, preventing unauthorized role management. The `requireValidRole` function correctly reverts with `AccessControlRoleExpired` if a role has passed its expiry, ensuring that expired access is denied. Ensure the `expiry` timestamp is set correctly to avoid unintended access durations.
- + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx index 9e0ee783..7945c0dd 100644 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "AccessControlTemporalMod" -description: "Manage time-bound role assignments in Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +description: "Manage roles with time-bound access control." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage time-bound role assignments in Compose diamonds. +Manage roles with time-bound access control. -- Grants roles with configurable expiry timestamps. -- Automatically enforces role validity, revoking expired assignments. -- Provides functions to check role expiry and revoke temporal roles. +- Grants roles with specific expiry timestamps. +- Automatically revokes expired roles, enforcing time-limited access. +- Provides functions to check role validity and expiry status. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module extends standard access control by introducing time-bound role assignments. It allows for roles to be granted with a specific expiry timestamp, automatically revoking them once that time passes. This enhances security and automates the cleanup of temporary permissions. +This module introduces time-bound access control, allowing roles to be granted with an expiry timestamp. It ensures that access is automatically revoked once the specified expiry is reached, enhancing security and simplifying access management for time-sensitive permissions within a diamond. --- @@ -242,7 +242,7 @@ function to check if a role assignment has expired. { name: "-", type: "bool", - description: "True if the role has expired or doesn\'t exist, false if still valid." + description: "True if the role has expired or doesn\'t exist, false if still valid." } ]} showRequired={false} @@ -433,29 +433,27 @@ error AccessControlUnauthorizedAccount(address _account, bytes32 _role); {`pragma solidity ^0.8.30; -import {IAccessControlTemporalMod} from "@compose/contracts/modules/AccessControlTemporalMod.sol"; -import {IDiamond} from "@compose/contracts/IDiamond.sol"; +import {IAccessControlTemporalMod} from "@compose/contracts/modules/access/AccessControlTemporalMod.sol"; -contract MyDiamondFacet { - IAccessControlTemporalMod internal accessControlTemporalMod; +contract MyFacet { + IAccessControlTemporalMod private constant _ACCESS_CONTROL_TEMPORAL = IAccessControlTemporalMod(address(this)); // Replace with actual diamond address - function initialize(IDiamond _diamond) external { - accessControlTemporalMod = IAccessControlTemporalMod(_diamond.getFacetAddress(address(this))); + function _performActionWithRole(address _account, bytes32 _role) internal { + // Check for valid and non-expired role + _ACCESS_CONTROL_TEMPORAL.requireValidRole(_account, _role); + + // Proceed with action if role is valid + // ... } - function grantTempAdmin(address _account, uint64 _expiry) external { - accessControlTemporalMod.grantRoleWithExpiry( - keccak256("ADMIN_ROLE"), - _account, - _expiry - ); + function _grantTemporaryRole(address _account, bytes32 _role, uint64 _expiry) external { + // Grant role with expiry + _ACCESS_CONTROL_TEMPORAL.grantRoleWithExpiry(_account, _role, _expiry); } - function checkAdminStatus(address _account) external view { - accessControlTemporalMod.requireValidRole( - keccak256("ADMIN_ROLE"), - _account - ); + function _revokeRole(address _account, bytes32 _role) external { + // Revoke temporal role + _ACCESS_CONTROL_TEMPORAL.revokeTemporalRole(_account, _role); } }`} @@ -463,19 +461,19 @@ contract MyDiamondFacet { ## Best Practices -- Use `requireValidRole` to enforce non-expired role checks before sensitive operations. -- Carefully manage expiry timestamps to prevent accidental role expiration or prolonged access. -- Leverage custom errors `AccessControlRoleExpired` and `AccessControlUnauthorizedAccount` for clear revert reasons. +- Use `requireValidRole` to enforce time-bound access control before executing sensitive operations. +- Ensure expiry timestamps are set appropriately to prevent indefinite access and manage role lifecycles effectively. +- Handle `AccessControlRoleExpired` and `AccessControlUnauthorizedAccount` errors to provide clear feedback to users. ## Integration Notes -This module interacts with the diamond's storage. Facets can access its functionality via the diamond proxy. Ensure the `AccessControlTemporalMod` facet is correctly initialized and accessible. The module manages role assignments and their expiry, which is critical for any access control logic within other facets. +The `AccessControlTemporalMod` interacts with diamond storage, requiring the presence of the underlying access control and temporal access control storage structures. Facets calling its functions will see the updated role assignments and expiry statuses. Ensure the module is initialized and correctly integrated into the diamond's facet registry. The `requireValidRole` function directly enforces access control logic, reverting if a role is unauthorized or expired.
- + diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx index 0f1a7968..9e92809c 100644 --- a/website/docs/library/access/AccessControlTemporal/index.mdx +++ b/website/docs/library/access/AccessControlTemporal/index.mdx @@ -1,6 +1,7 @@ --- title: "Temporal Access Control" description: "Time-limited role-based access control." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx index 95205243..0944dd54 100644 --- a/website/docs/library/access/Owner/OwnerFacet.mdx +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerFacet" -description: "Manages contract ownership and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/Owner/OwnerFacet.sol" +description: "Manages diamond ownership and transfers." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages contract ownership and transfers. +Manages diamond ownership and transfers. -- Standard ERC-173 ownership pattern implementation. -- Provides explicit functions for owner retrieval, transfer, and renunciation. -- Supports ownership transfer to `address(0)` for renunciation. +- Provides owner address retrieval. +- Supports ownership transfer to a new address. +- Allows for the renunciation of ownership. ## Overview -The OwnerFacet provides standard ownership management functions for a Compose diamond. It allows for the retrieval of the current owner, the transfer of ownership to a new address, and the renunciation of ownership. This facet is crucial for controlling administrative functions within the diamond. +The OwnerFacet provides essential functions for managing the ownership of a Compose diamond. It allows querying the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative actions within the diamond. --- @@ -61,28 +61,6 @@ The OwnerFacet provides standard ownership management functions for a Compose di ## Functions -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- ### owner Get the address of the owner @@ -167,24 +145,24 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerFacet} from "@compose/facets/owner/IOwnerFacet.sol"; +import {IOwnerFacet} from "../interfaces/IOwnerFacet.sol"; -contract OwnerConsumer { - IOwnerFacet public ownerFacet; - - constructor(address _ownerFacetAddress) { - ownerFacet = IOwnerFacet(_ownerFacetAddress); - } +contract Deployer { + // Assume diamondAbi is the ABI of your diamond + // Assume diamondAddress is the address of your deployed diamond + IOwnerFacet ownerFacet = IOwnerFacet(diamondAddress); function getCurrentOwner() external view returns (address) { return ownerFacet.owner(); } function transferDiamondOwnership(address _newOwner) external { + // Ensure you have the necessary permissions to call this function ownerFacet.transferOwnership(_newOwner); } function renounceDiamondOwnership() external { + // Ensure you have the necessary permissions to call this function ownerFacet.renounceOwnership(); } }`} @@ -193,19 +171,19 @@ contract OwnerConsumer { ## Best Practices -- Initialize ownership during diamond deployment to a trusted address. -- Only transfer ownership to addresses that are prepared to manage the diamond's administrative functions. -- Use `renounceOwnership` with extreme caution, as it permanently removes the owner. +- Use `transferOwnership` to safely delegate control by setting a new owner. +- Call `renounceOwnership` with extreme caution, as it makes the diamond effectively un-administerable. +- Ensure that only authorized entities can call `transferOwnership` and `renounceOwnership`. ## Security Considerations -Ownership is a critical administrative role. Ensure that ownership transfers are authorized and that the new owner is a secure and trusted address. The `transferOwnership` function can be used to renounce ownership by setting the `_newOwner` to `address(0)`. Unauthorized access to ownership transfer functions could lead to a loss of control over the diamond. +Ownership transfer functions (`transferOwnership`, `renounceOwnership`) are highly sensitive. Ensure that calls to these functions are restricted to the current owner or a designated administrative role to prevent unauthorized control of the diamond. The `transferOwnership` function allows setting the new owner to `address(0)` to effectively renounce ownership, which should be handled with care.
- + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx index 9f0a3b97..dcea3566 100644 --- a/website/docs/library/access/Owner/OwnerMod.mdx +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerMod" -description: "Manages contract ownership according to ERC-173." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/Owner/OwnerMod.sol" +description: "Manages ERC-173 contract ownership and access control." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,14 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages contract ownership according to ERC-173. +Manages ERC-173 contract ownership and access control. -- Implements ERC-173 standard for contract ownership. -- Provides `owner()` view function to retrieve the current owner. -- Includes `requireOwner()` for access control, reverting if the caller is not the owner. -- Supports ownership transfer and renouncement via `transferOwnership()`. +- ERC-173 compliant ownership tracking. +- Functions for retrieving, transferring, and renouncing ownership. +- Access control enforcement via `requireOwner`. @@ -37,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The OwnerMod provides the foundational storage and functions for ERC-173 contract ownership. It enables secure owner management, including transferring ownership and renouncing it. This module is crucial for establishing administrative control within a diamond, ensuring that critical operations can be restricted to authorized entities. +The OwnerMod provides essential functionality for managing contract ownership according to the ERC-173 standard. It defines storage for the owner's address and offers functions to retrieve, transfer, and enforce ownership, crucial for secure administrative operations within a Compose diamond. --- @@ -208,28 +207,28 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; -import {OwnerStorage} from "@compose/modules/owner/OwnerStorage.sol"; +import {IOwnerMod} from "@compose/modules/OwnerMod/IOwnerMod.sol"; +import {OwnerStorage} from "@compose/modules/OwnerMod/OwnerStorage.sol"; contract MyOwnerFacet { - // Assuming OwnerMod storage is deployed at STORAGE_POSITION - uint256 constant STORAGE_POSITION = 1; + uint256 constant OWNER_STORAGE_POSITION = 1; // Example storage slot - function getOwner() external view returns (address) { - OwnerStorage storage storageSlot = OwnerStorage(STORAGE_POSITION); - return storageSlot.owner; + function getOwner() public view returns (address) { + // Access storage directly or via a helper if provided by the module + // This example assumes direct access to storage for demonstration + OwnerStorage storage ownerStorage = OwnerStorage(OWNER_STORAGE_POSITION); + return ownerStorage.owner; } - function transferContractOwnership(address _newOwner) external { - IOwnerMod(msg.sender).transferOwnership(_newOwner); + function transferOwner(address _newOwner) external { + // Assuming OwnerMod provides an interface or direct access + // This is a conceptual call, actual implementation depends on module's facet interface + IOwnerMod(OWNER_STORAGE_POSITION).transferOwnership(_newOwner); } - function renounceContractOwnership() external { - IOwnerMod(msg.sender).transferOwnership(address(0)); - } - - function requireCallerIsOwner() external view { - IOwnerMod(msg.sender).requireOwner(); + function requireIsOwner() external view { + // Assuming OwnerMod provides an interface or direct access + IOwnerMod(OWNER_STORAGE_POSITION).requireOwner(); } }`} @@ -237,19 +236,19 @@ contract MyOwnerFacet { ## Best Practices -- Only the current owner should be able to call `transferOwnership` or renounce ownership. -- Use `transferOwnership` with `address(0)` to safely renounce ownership, preventing accidental lockouts. -- Facets interacting with ownership should explicitly check the owner using `requireOwner` or by reading the owner address. +- Use `transferOwnership` to safely transfer ownership, setting the new owner's address. +- Set the owner to `address(0)` to renounce ownership when necessary, making the contract permissionless. +- Utilize `requireOwner` within administrative functions to enforce access control and prevent unauthorized actions. ## Integration Notes -The OwnerMod utilizes a dedicated storage slot (defined by `STORAGE_POSITION` in `OwnerStorage.sol`) to store the `OwnerStorage` struct. Any facet that needs to interact with ownership functions or check the owner's address must be aware of this storage layout and access it appropriately, typically by referencing the `IOwnerMod` interface and ensuring the module is deployed at the correct slot within the diamond's storage map. +The OwnerMod reserves a specific storage slot (defined by `STORAGE_POSITION`) for its `OwnerStorage` struct. Facets interacting with ownership should access this storage slot. Changes to the owner address are immediately visible to all facets through this shared storage. The `setContractOwner` function is intended for initial deployment or specific administrative scenarios, while `transferOwnership` is the standard method for ongoing ownership management.
- + diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx index 80a2ad8a..4ecc8496 100644 --- a/website/docs/library/access/Owner/index.mdx +++ b/website/docs/library/access/Owner/index.mdx @@ -1,6 +1,7 @@ --- title: "Owner" description: "Single-owner access control pattern." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx index c4ee03b6..aa14912c 100644 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "OwnerTwoStepsFacet" description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -26,13 +26,14 @@ Manages contract ownership with a two-step transfer process. - Two-step ownership transfer process for enhanced security. -- `owner()` and `pendingOwner()` view functions to check current and proposed owners. -- `renounceOwnership()` function to remove ownership entirely. +- Explicit `acceptOwnership` call by the new owner. +- `renounceOwnership` function to remove ownership. +- Direct storage access via inline assembly for efficiency. ## Overview -This facet provides a robust ownership management system for Compose diamonds, enforcing a two-step transfer process to prevent accidental ownership loss. It allows the current owner to initiate a transfer and requires the new owner to explicitly accept it, ensuring secure control over critical diamond functions. +This facet provides a secure, two-step ownership transfer mechanism, preventing accidental or malicious takeover of the diamond. It allows the current owner to initiate a transfer and requires the new owner to accept it, ensuring explicit confirmation. --- @@ -75,50 +76,6 @@ This facet provides a robust ownership management system for Compose diamonds, e ## Functions -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- ### owner Get the address of the owner @@ -242,27 +199,28 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {IOwnerTwoStepsFacet} from "@compose/facets/OwnerTwoStepsFacet.sol"; +import {IOwnerTwoStepsFacet} from "@compose/contracts/src/facets/owner/IOwnerTwoStepsFacet.sol"; -contract MyDiamond { - IOwnerTwoStepsFacet ownerFacet; +contract OwnerConsumer { + IOwnerTwoStepsFacet public ownerFacet; - // Assume ownerFacet is initialized and its selector is registered + constructor(address _ownerFacetAddress) { + ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); + } - function proposeNewOwner(address _newOwner) external { + function initiateOwnershipTransfer(address _newOwner) external { + // Assumes caller is the current owner ownerFacet.transferOwnership(_newOwner); } function acceptNewOwnership() external { + // Assumes caller is the pending owner ownerFacet.acceptOwnership(); } - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function getPendingOwnerAddress() external view returns (address) { - return ownerFacet.pendingOwner(); + function renounceContractOwnership() external { + // Assumes caller is the current owner + ownerFacet.renounceOwnership(); } }`} @@ -270,19 +228,19 @@ contract MyDiamond { ## Best Practices -- Initialize the `OwnerTwoStepsFacet` with the desired initial owner address during diamond deployment. -- Use the `transferOwnership` function to initiate a change, and ensure the new owner calls `acceptOwnership` to finalize the transfer. -- Store the `OwnerTwoStepsFacet` interface in your diamond contract or a dedicated facets registry for easy access. +- Only the current owner should call `transferOwnership` and `renounceOwnership`. +- Only the pending owner should call `acceptOwnership`. +- Store the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` constants securely and ensure they are correctly set during deployment. ## Security Considerations -Only the current owner can initiate ownership transfers. Any account can accept a pending ownership transfer. Ensure that the address calling `transferOwnership` is indeed the intended owner. Consider the implications of `renounceOwnership` as it makes the contract ownership unrecoverable. +Ensure that the `transferOwnership` function is only callable by the current owner. The `acceptOwnership` function must only be callable by the pending owner. Incorrect access control could lead to unauthorized ownership changes. The inline assembly for storage access relies on the correct definition and immutability of `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION`; any change to these slots could break ownership management.
- + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx index efe8fa09..361c4d3d 100644 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "OwnerTwoStepsMod" -description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +description: "Manages ERC-173 contract ownership with a two-step transfer process." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages contract ownership with a two-step transfer process. +Manages ERC-173 contract ownership with a two-step transfer process. -- Implements a secure two-step ownership transfer via `transferOwnership` and `acceptOwnership`. -- Provides `owner()` and `pendingOwner()` view functions to query current and pending ownership. -- Includes `renounceOwnership()` to permanently disable owner-restricted functions. +- Implements a secure two-step ownership transfer process. +- Provides functions to view current and pending owners. +- Includes a `requireOwner` guard for owner-only operations. +- Supports `renounceOwnership` to disable owner-specific functionality. @@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module implements a secure two-step ownership transfer mechanism, ensuring that ownership changes are deliberate and auditable. By requiring explicit acceptance from the new owner, it mitigates risks associated with accidental or malicious ownership transfers, enhancing the overall safety and composability of your diamond. +This module provides a secure, two-step ownership transfer mechanism compliant with ERC-173. It ensures that ownership changes can only be finalized by the intended new owner, preventing accidental or malicious transfers. This pattern is crucial for managing administrative functions within a diamond proxy. --- @@ -252,23 +253,31 @@ error OwnerUnauthorizedAccount(); {`pragma solidity ^0.8.30; -import {OwnerTwoStepsMod} from "../OwnerTwoStepsMod.sol"; +import {IOwnerTwoSteps} from "@compose/contracts/modules/owner/IOwnerTwoSteps.sol"; -contract MyFacet is OwnerTwoStepsMod { - // Assume OWNER_STORAGE_POSITION and PENDING_OWNER_STORAGE_POSITION are defined and set - // in the diamond deployment. +contract MyOwnerFacet { + IOwnerTwoSteps public ownerFacet; - function someOwnerRestrictedFunction() external { - requireOwner(); // Ensure only the owner can call this function - // ... owner-only logic ... + // Assuming ownerFacet is initialized elsewhere and points to the OwnerTwoStepsMod + + function transferAdmin(address _newOwner) external { + ownerFacet.transferOwnership(_newOwner); + } + + function acceptAdmin() external { + ownerFacet.acceptOwnership(); + } + + function getCurrentOwner() external view returns (address) { + return ownerFacet.owner(); } - function initiateOwnershipTransfer(address _newOwner) external { - transferOwnership(_newOwner); + function getCurrentPendingOwner() external view returns (address) { + return ownerFacet.pendingOwner(); } - function acceptNewOwnership() external { - acceptOwnership(); + function renounceAdmin() external { + ownerFacet.renounceOwnership(); } }`} @@ -276,19 +285,19 @@ contract MyFacet is OwnerTwoStepsMod { ## Best Practices -- Use `requireOwner()` judiciously to protect critical functions, ensuring only the designated owner can execute sensitive operations. -- Handle `OwnerUnauthorizedAccount` and `OwnerAlreadyRenounced` errors appropriately in your calling contracts or frontend applications. -- Be aware that `renounceOwnership()` permanently relinquishes owner privileges; ensure this action is intended and irreversible. +- Use `transferOwnership` to initiate a transfer, followed by `acceptOwnership` by the new owner. +- Protect critical administrative functions using `requireOwner` or by checking the `owner()` return value. +- Be aware that `renounceOwnership` permanently relinquishes ownership, setting the owner to `address(0)`. ## Integration Notes -The `OwnerTwoStepsMod` relies on specific storage slots for its `Owner` and `PendingOwner` state variables. These slots are typically defined as constants (e.g., `OWNER_STORAGE_POSITION`, `PENDING_OWNER_STORAGE_POSITION`) within the diamond's facet deployment. Facets interacting with this module can access the owner information via the `owner()` and `pendingOwner()` view functions. The `requireOwner()` internal function enforces access control by checking against the current owner's address. +The `OwnerTwoStepsMod` stores ownership data in dedicated storage slots. Facets interacting with this module should use the provided accessor functions (`owner`, `pendingOwner`) to retrieve ownership information. The module's storage layout is fixed and should not be altered by other facets to maintain invariant integrity.
- + diff --git a/website/docs/library/access/OwnerTwoSteps/index.mdx b/website/docs/library/access/OwnerTwoSteps/index.mdx index 0b051554..a02ceef6 100644 --- a/website/docs/library/access/OwnerTwoSteps/index.mdx +++ b/website/docs/library/access/OwnerTwoSteps/index.mdx @@ -1,6 +1,7 @@ --- title: "Two-Step Owner" description: "Two-step ownership transfer pattern." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -21,7 +22,7 @@ import Icon from '@site/src/components/ui/Icon'; /> } size="medium" diff --git a/website/docs/library/access/index.mdx b/website/docs/library/access/index.mdx index 1e83a09d..edf619c1 100644 --- a/website/docs/library/access/index.mdx +++ b/website/docs/library/access/index.mdx @@ -1,6 +1,7 @@ --- title: "Access Control" description: "Access control patterns for permission management in Compose diamonds." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx index b396283c..9272f3b1 100644 --- a/website/docs/library/diamond/DiamondCutFacet.mdx +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondCutFacet" -description: "Manage diamond facets and function registrations." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondCutFacet.sol" +description: "Manage diamond facets and functions" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage diamond facets and function registrations. +Manage diamond facets and functions -- Supports adding, replacing, and removing facets and their associated functions. -- Enables atomic upgrades with the `diamondCut` function. -- Provides owner-only control over diamond modifications. +- Atomic addition, replacement, and removal of facets and functions. +- Supports executing an initialization function after a diamond cut. +- Provides access to diamond storage related to facet management. ## Overview -The DiamondCutFacet provides essential functions for managing the diamond's functionality. It allows for adding, replacing, and removing facets, as well as updating function selectors. This facet is crucial for diamond upgrades and maintaining its modular structure. +The DiamondCutFacet provides essential functions for managing the diamond's upgradeability and function routing. It allows for the addition, replacement, and removal of facets and their associated functions, along with optional initialization execution. This facet is central to the Compose diamond's dynamic nature and extensibility. --- @@ -100,110 +100,6 @@ The DiamondCutFacet provides essential functions for managing the diamond's func ## Functions -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- ### diamondCut Add/replace/remove any number of functions and optionally execute a function with delegatecall @@ -368,27 +264,40 @@ error InitializationFunctionReverted(address _initializationContractAddress, byt {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose/diamond/contracts/facets/DiamondCutFacet.sol"; +import {DiamondCutFacet} from "@compose-protocol/diamond-contracts/facets/DiamondCutFacet.sol"; +import {IDiamondCut} from "@compose-protocol/diamond-contracts/interfaces/IDiamondCut.sol"; -contract Deployer { - address immutable DIAMOND_PROXY; +contract DeployDiamond { + // Assume diamondProxy is already deployed and initialized + address diamondProxy; - constructor(address _diamondProxy) { - DIAMOND_PROXY = _diamondProxy; - } - - function upgradeDiamond() external { - IDiamondCut diamondCut = IDiamondCut(DIAMOND_PROXY); + function upgradeDiamond() public { + DiamondCutFacet diamondCutFacet = DiamondCutFacet(diamondProxy); // Example: Add a new facet - bytes[] memory facetCuts = new bytes[](1); - // Assume facetCuts[0] contains encoded data for adding a facet - // diamondCut.diamondCut(facetCuts, address(0), "", false); - - // Example: Replace a function - // bytes[] memory replaceCuts = new bytes[](1); - // Assume replaceCuts[0] contains encoded data for replacing a function - // diamondCut.diamondCut(new bytes[](0), address(0), "", false); + bytes memory facetImplementation = type(MyNewFacet).creationCode; + address facetAddress = address(new MyNewFacet()); // Or deploy and get address + + // Call addFunctions to add the new facet and its functions + diamondCutFacet.diamondCut( + IDiamondCut.FacetCut[]( + IDiamondCut.FacetCut( + facetAddress, + IDiamondCut.FacetCutAction.ADD, + bytes4(keccak256("myNewFunction() +")) // Selector for myNewFunction + ) + ), + address(0), // Init address + "" // Init calldata + ); + } + + // Example of a custom facet + contract MyNewFacet { + function myNewFunction() external pure { + // ... implementation ... + } } }`} @@ -396,19 +305,19 @@ contract Deployer { ## Best Practices -- Ensure the caller is authorized before performing any cut operations. -- Carefully manage function selector registrations to avoid conflicts or unintended overwrites. -- Leverage `diamondCut`'s ability to execute an initialization function post-upgrade for proper state setup. +- Ensure that only authorized addresses can call `diamondCut` functions. This typically involves access control mechanisms within the diamond's ownership or governance setup. +- When replacing or removing functions, carefully consider the potential impact on existing interactions with the diamond to avoid breaking changes for consumers. +- Use the `diamondCut` function to add, replace, or remove multiple functions atomically. This ensures that the diamond's function map remains consistent. ## Security Considerations -This facet is highly sensitive as it modifies the diamond's core functionality. Access control is paramount; only authorized addresses (typically the diamond's owner) should be able to call `diamondCut` and related functions. Incorrectly managing function selectors can lead to unexpected behavior or denial of service. Reentrancy is not an immediate concern for the cut operations themselves, but any initialization function called via `diamondCut` must be reentrancy-guarded. +The `diamondCut` function is a powerful administrative function. Unauthorized access could lead to the diamond being rendered inoperable or its functionality being compromised. Ensure robust access control is implemented for this function. Reentrancy is not a direct concern for `diamondCut` itself, but any initialization function called via `diamondCut` must be reentrancy-guarded if it interacts with external contracts or modifies state that could be re-entered.
- + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx index db9bb672..65f69a56 100644 --- a/website/docs/library/diamond/DiamondCutMod.mdx +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondCutMod" -description: "Manages diamond facet additions, replacements, and removals." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondCutMod.sol" +description: "Manage diamond facets and function registrations." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages diamond facet additions, replacements, and removals. +Manage diamond facets and function registrations. -- Atomically adds, replaces, or removes multiple functions in a single transaction. -- Supports optional delegatecall execution of an initialization function after the cut. -- Enforces restrictions against modifying immutable functions and prevents redundant operations. +- Atomic `diamondCut` for adding, replacing, and removing multiple functions in a single transaction. +- Supports delegatecalling an initialization function during a diamond cut operation. +- Emits a `DiamondCut` event upon successful execution of facet modifications. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The DiamondCutMod provides essential functions for managing the facets of a Compose diamond. It allows for controlled addition, replacement, and removal of functions, ensuring the diamond's logic can be upgraded and extended safely. This module is critical for maintaining the diamond's integrity during upgrades. +The DiamondCutMod provides essential functions for managing the diamond's facets. It allows for the addition, replacement, and removal of function selectors, enabling dynamic upgrades and modifications to the diamond's capabilities. This module is crucial for maintaining and evolving the diamond's functionality post-deployment. --- @@ -334,41 +334,42 @@ error RemoveFacetAddressMustBeZeroAddress(address _facet); {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/contracts/facets/DiamondCutMod.sol"; +import {DiamondCutMod} from "@compose/diamond-proxy/modules/diamond-cut/DiamondCutMod.sol"; +import {IDiamondCut} from "@compose/diamond-proxy/interfaces/IDiamondCut.sol"; contract MyFacet { - // ... other facet logic ... - - function upgradeDiamond(address _diamondAddress) external { - address[] memory facetAddresses = new address[](1); - facetAddresses[0] = address(this); // Assuming this contract is the new facet - - bytes[] memory functionSigs = new bytes[](1); - functionSigs[0] = bytes4(keccak256(\"myNewFunction(uint256)\")); // Example selector - - IDiamondCut(_diamondAddress).diamondCut(facetAddresses, address(0), functionSigs, address(0), \"\"); + // Assume DiamondCutMod is already added to the diamond + + function upgradeDiamond(address _diamondCutAddress) external { + // Example: Add a new function + IDiamondCut(_diamondCutAddress).addFunctions( + address(this), + bytes4[](uint256(0), IDiamondCut.myNewFunction.selector) + ); } - // ... other facet logic ... + function myNewFunction() external pure { + // ... implementation ... + } }`} ## Best Practices -- Use `diamondCut` carefully; ensure all function selectors are correctly identified to avoid unintended logic changes or access control bypasses. -- Always verify that the facet addresses provided are deployed and contain the intended bytecode before executing `diamondCut`. -- Handle potential `InitializationFunctionReverted` errors by ensuring any initialization logic within the cut is robust and correctly implemented. +- Use `diamondCut` for all facet and function management operations to ensure atomicity and proper event emission. +- Be aware of the `CannotRemoveImmutableFunction` error and ensure you do not attempt to remove functions marked as immutable. +- Thoroughly test facet additions and removals in a staging environment before applying to production diamonds. ## Integration Notes -The DiamondCutMod directly manipulates the diamond's function selector to facet address mapping. Any changes made via `diamondCut` are immediately reflected in the diamond proxy's routing logic. Facets interact with this module by calling its external functions, typically during upgrade processes. The `DiamondCut` event is emitted upon successful completion of a diamond cut operation, providing an audit trail of changes. +The DiamondCutMod interacts directly with the diamond's storage to register and unregister function selectors. Facets added or modified through this module become immediately available via the diamond proxy. Ensure that the DiamondCutMod is initialized correctly and that its storage slot is not conflicted with by other modules. The order of operations within a single `diamondCut` call is important for managing dependencies between facet additions and removals.
- + diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx index c51bd1d8..d539c37e 100644 --- a/website/docs/library/diamond/DiamondInspectFacet.mdx +++ b/website/docs/library/diamond/DiamondInspectFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondInspectFacet" -description: "Inspect diamond storage and function-to-facet mappings." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondInspectFacet.sol" +description: "Inspect diamond storage and facet mappings." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondInspectFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Inspect diamond storage and function-to-facet mappings. +Inspect diamond storage and facet mappings. -- Directly retrieves raw diamond storage using inline assembly. -- Maps function selectors to their implementing facet addresses. -- Provides read-only access, ensuring state integrity. +- Directly accesses diamond storage via inline assembly. +- Provides a comprehensive mapping of function selectors to facet addresses. +- Read-only operations, ensuring no state modification. ## Overview -The DiamondInspectFacet provides essential read-only capabilities for understanding a Compose diamond's internal state and function distribution. It allows developers to query the diamond's storage layout and map function selectors to their respective facet implementations, aiding in debugging and integration. +The DiamondInspectFacet provides essential read-only capabilities for understanding a Compose diamond's internal state and function distribution. It allows developers to query the diamond's core storage structure and map function selectors to their respective implementation facets. --- @@ -85,28 +85,6 @@ The DiamondInspectFacet provides essential read-only capabilities for understand ## Functions -### getStorage - -Retrieves the diamond's storage struct from its fixed position. Uses inline assembly to access the storage slot directly. - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - -**Returns:** - - - ---- ### functionFacetPairs Returns an array of all function selectors and their corresponding facet addresses. Iterates through the diamond's stored selectors and pairs each with its facet. @@ -122,7 +100,7 @@ Returns an array of all function selectors and their corresponding facet address { name: "pairs", type: "FunctionFacetPair[]", - description: "An array of `FunctionFacetPair` structs, each containing a selector and its facet address." + description: "An array of `FunctionFacetPair` structs, each containing a selector and its facet address." } ]} showRequired={false} @@ -133,24 +111,21 @@ Returns an array of all function selectors and their corresponding facet address {`pragma solidity ^0.8.30; -import {DiamondInspectFacet} from "@compose/diamond-facet-inspect/DiamondInspectFacet.sol"; -import {IDiamondCut} from "@compose/diamond-contracts/contracts/interfaces/IDiamondCut.sol"; +import {IDiamondInspect} from "@compose/contracts/facets/DiamondInspect/IDiamondInspect.sol"; contract Consumer { - address immutable diamondAddress; + IDiamondInspect immutable diamondInspect; constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; + diamondInspect = IDiamondInspect(_diamondAddress); } - function inspectStorage() external view returns (bytes memory) { - DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamondAddress); - return inspectFacet.getStorage(); + function inspectStorage() public view returns (bytes memory) { + return diamondInspect.getStorage(); } - function getFunctionFacetPairs() external view returns (IDiamondCut.FacetPair[] memory) { - DiamondInspectFacet inspectFacet = DiamondInspectFacet(diamondAddress); - return inspectFacet.functionFacetPairs(); + function inspectFunctionMappings() public view returns (struct IDiamondInspect.FunctionFacetPair[] memory) { + return diamondInspect.functionFacetPairs(); } }`} @@ -158,19 +133,19 @@ contract Consumer { ## Best Practices -- Integrate this facet into your diamond to enable introspection capabilities. -- Access functions via the diamond proxy address to ensure correct routing. -- Use the retrieved data for debugging, auditing, and dynamic integration. +- Integrate this facet to audit diamond state and function assignments. +- Use `getStorage()` cautiously; its output is a raw storage slot and requires understanding of the diamond's storage layout. +- `functionFacetPairs()` is invaluable for debugging and understanding runtime dispatch. ## Security Considerations -This facet is read-only and does not modify state, mitigating reentrancy risks. Access control is managed by the diamond proxy itself. Ensure the diamond's access control mechanisms are robust, as this facet exposes internal mappings. +The `getStorage()` function returns raw storage data. Misinterpretation or misuse of this data could lead to incorrect assumptions about the diamond's state. Ensure that any logic relying on `getStorage()` is robust and accounts for potential storage layout changes during upgrades. `functionFacetPairs()` is generally safe, but the addresses returned should be validated if used in sensitive operations.
- + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx index 616a43d5..55b39881 100644 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondLoupeFacet" -description: "Inspect diamond facets, selectors, and storage" -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondLoupeFacet.sol" +description: "Inspect diamond facets, functions, and storage locations." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondLoupeFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,17 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Inspect diamond facets, selectors, and storage +Inspect diamond facets, functions, and storage locations. -- Provides a comprehensive view of diamond's facets and their associated selectors. -- Optimized for gas efficiency when querying large diamonds with many facets and selectors. +- Provides read-only access to diamond's facet and function mappings. +- Optimized for gas efficiency when querying large numbers of facets and selectors. +- Supports querying for specific facet addresses or all deployed facet addresses. ## Overview -The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, the function selectors they support, and the addresses of these facets. This facet is crucial for understanding the diamond's composition and for dynamic contract interactions. +The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, what functions each facet supports, and the addresses of these facets. This facet is crucial for understanding the diamond's internal structure and for dynamic contract interactions. --- @@ -84,13 +85,6 @@ The DiamondLoupeFacet provides essential introspection capabilities for a diamon ## Functions -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- ### facetAddress Gets the facet address that supports the given selector. If facet is not found return address(0). @@ -154,7 +148,7 @@ Gets all the function selectors supported by a specific facet. Returns the set o { name: "facetSelectors", type: "bytes4[]", - description: "The function selectors implemented by `_facet`." + description: "The function selectors implemented by `_facet`." } ]} showRequired={false} @@ -209,23 +203,27 @@ Gets all facets and their selectors. Returns each unique facet address currently {`pragma solidity ^0.8.30; -import {IDiamondLoupe} from "../facets/DiamondLoupeFacet.sol"; +import {DiamondLoupeFacet} from "@compose/diamond-facets/DiamondLoupeFacet.sol"; -contract DiamondConsumer { - address immutable diamondAddress; +contract MyDiamond { + DiamondLoupeFacet public diamondLoupeFacet; - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; + function initDiamond(address[] memory _facetAddresses, bytes4[][] memory _facetSelectors) external { + // ... deployment logic ... + diamondLoupeFacet = DiamondLoupeFacet(address(this)); } - function getDiamondFacets() public view returns (IDiamondLoupe.Facet[] memory) { - IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); - return loupe.facets(); + // Example of calling a function from DiamondLoupeFacet + function getFunctionSelectors(address _facetAddress) external view returns (bytes4[] memory) { + return diamondLoupeFacet.facetFunctionSelectors(_facetAddress); } - function getFacetAddress(bytes4 _selector) public view returns (address) { - IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); - return loupe.facetAddress(_selector); + function getAllFacets() external view returns ( + address[] memory facetAddresses, + address[] memory facetAddresses, + bytes4[][] memory facetSelectors + ) { + return diamondLoupeFacet.facets(); } }`} @@ -233,18 +231,19 @@ contract DiamondConsumer { ## Best Practices -- Integrate DiamondLoupeFacet into your diamond to enable introspection for developers and external systems. -- Use the returned data to dynamically route calls or to verify diamond state during upgrades. +- Deploy DiamondLoupeFacet as part of the initial diamond deployment to ensure introspection capabilities are available from the start. +- Use the `facets` function to get a comprehensive view of the diamond's composition during development and auditing. +- Cache facet addresses and selectors in off-chain applications for performance, rather than repeatedly querying the diamond. ## Security Considerations -This facet is read-only and does not modify state. Its primary security concern is the accuracy of the data it returns, which relies on the correct implementation of diamond proxy logic for tracking facets and selectors. +This facet is read-only and does not modify state, posing minimal direct security risks. However, the information it provides can be used to identify potential attack vectors if not properly secured by other facets. Ensure that access control for state-modifying functions is handled by the respective facets, not this introspection facet.
- + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx index d009a44f..7e82c958 100644 --- a/website/docs/library/diamond/DiamondMod.mdx +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "DiamondMod" -description: "Manage diamond facets and internal storage." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/DiamondMod.sol" +description: "Manages diamond proxy facets and internal state." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage diamond facets and internal storage. +Manages diamond proxy facets and internal state. -- Facet management: Allows adding facets and their function selectors to the diamond. -- Function routing: Provides a fallback mechanism to dispatch calls to the correct facet. -- Internal storage access: Exposes a method to retrieve the diamond's internal storage slot. +- Supports adding facets and their selectors exclusively during diamond deployment. +- Implements a fallback mechanism to route function calls to the appropriate facet. +- Provides access to the diamond's internal storage layout. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides core functionality for managing facets within a diamond proxy. It enables adding new facets during deployment and offers a fallback mechanism to route external calls to the appropriate facet, ensuring composability and upgradability. +The DiamondMod module provides essential internal functions for managing facets within a Compose diamond. It handles adding new facets during deployment and enables the diamond's fallback mechanism to route external calls to the correct facet, ensuring composability and extensibility. --- @@ -195,17 +195,19 @@ error NoBytecodeAtAddress(address _contractAddress, string _message); {`pragma solidity ^0.8.30; -import {IDiamondMod} from "@compose/diamond-proxy/contracts/modules/DiamondMod.sol"; +import {IDiamondMod} from "@compose/diamond-proxy/src/diamond-proxy/IDiamondMod.sol"; contract MyFacet { - IDiamondMod public diamondMod; + IDiamondMod internal diamondMod; - function initialize(address _diamondMod) external { + constructor(address _diamondMod) { diamondMod = IDiamondMod(_diamondMod); } - function getDiamondStorage() external view returns (bytes32) { - // Example of accessing storage via the module + /** + * @notice Example of calling getStorage. + */ + function readInternalStorage() external view returns (bytes memory) { return diamondMod.getStorage(); } }`} @@ -214,19 +216,19 @@ contract MyFacet { ## Best Practices -- Use `addFacets` exclusively during initial diamond deployment to avoid unexpected state changes. -- Implement robust error handling for `diamondFallback` to gracefully manage unknown function selectors. -- Understand that `getStorage` provides access to the diamond's internal storage slot, which is fundamental to the diamond pattern. +- Facet addition is restricted to the initial diamond deployment phase to maintain consistency. +- Utilize `diamondFallback` for internal call routing; ensure function selectors are correctly registered. +- Handle potential errors like `FunctionNotFound` and `CannotAddFunctionToDiamondThatAlreadyExists`. ## Integration Notes -This module manages the diamond's internal mapping of function selectors to facet addresses. Facets can interact with this module to retrieve the diamond's storage slot via `getStorage()`. The `addFacets` function is intended for use only during the initial deployment of the diamond contract. Calling it after deployment can lead to the `InvalidActionWhenDeployingDiamond` error. +DiamondMod interacts directly with the diamond's storage contract. The `addFacets` function is intended for use only during the initial deployment of a diamond to register facets and their associated function selectors. The `diamondFallback` function reads the facet cut information from storage to determine the correct facet for executing incoming calls. `getStorage` returns the raw storage slot of the diamond's internal state.
- + diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx index 041e5ec5..86ec8496 100644 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ExampleDiamond" -description: "Example Diamond for Compose diamond framework" -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/diamond/example/ExampleDiamond.sol" +description: "Example Diamond for Compose, demonstrating core proxy functionality." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/example/ExampleDiamond.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Example Diamond for Compose diamond framework +Example Diamond for Compose, demonstrating core proxy functionality. -- Demonstrates core diamond proxy structure and initialization. -- Supports facet registration and owner assignment. -- Provides a basic fallback and receive function for Ether handling. +- Core diamond proxy functionality for facet routing. +- Initializes diamond with owner and facet mappings. +- Demonstrates basic facet registration and ownership setup. ## Overview -The ExampleDiamond contract serves as a foundational template within the Compose diamond framework. It demonstrates core diamond proxy functionalities, including facet registration and owner initialization, providing a starting point for custom diamond deployments. +ExampleDiamond serves as a foundational implementation for Compose diamonds. It showcases the basic proxy logic for routing function calls to registered facets and includes essential initialization capabilities for setting up diamond ownership and facet mappings. --- @@ -85,42 +85,44 @@ Struct to hold facet address and its function selectors. struct FacetCut { {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose/diamond-cut/src/interfaces/IDiamondCut.sol"; -import {DiamondInit} from "@compose/diamond-init/src/DiamondInit.sol"; +import {IDiamondCut, IDiamondLoupe} from "@compose-diamond/diamond-contracts/contracts/interfaces/IDiamond.sol"; +import {ExampleDiamond} from "@compose-diamond/diamond-contracts/contracts/ExampleDiamond.sol"; -contract MyDiamond is IDiamondCut { - address owner; +contract Deployer { + address owner = msg.sender; - constructor(address _owner, DiamondInit _diamondInit) { - owner = _owner; - // Example of calling DiamondInit to add facets - // _diamondInit.diamondCut(...); // This would typically happen in a separate deployment script - } + function deploy() public { + // Example facets (replace with actual facet addresses and selectors) + address[] memory facetAddresses = new address[](1); + facetAddresses[0] = address(0x123); // Replace with actual facet address + + bytes4[][] memory facetSelectors = new bytes4[][](1); + facetSelectors[0] = new bytes4[](1); + facetSelectors[0][0] = ExampleDiamond.exampleFunction.selector; // Replace with actual function selector - // Fallback and receive functions would be implemented here or in facets - fallback() external payable {} - receive() external payable {} + ExampleDiamond diamond = new ExampleDiamond(facetAddresses, facetSelectors, owner); - // Other diamond functions or facet functions would be exposed via delegatecall + // Interactions with the deployed diamond would follow here + } }`} ## Best Practices -- Use `DiamondInit` or a similar mechanism for facet installation during deployment rather than directly in the constructor for better upgradeability and clarity. -- Ensure the owner is set correctly during initialization to manage diamond upgrades and facet management. -- Implement `fallback` and `receive` functions explicitly or ensure they are handled by registered facets to manage arbitrary calls and Ether reception. +- Initialize the diamond with the owner and all initial facets during deployment. +- Ensure all facets intended for use are correctly registered with their function selectors. +- Use the `IDiamondCut` interface for managing facet additions, replacements, or removals in upgrade scenarios. ## Security Considerations -The constructor directly sets the owner. Ensure this initialization is performed securely and by a trusted entity. The `fallback` and `receive` functions, if not implemented in facets, will allow any call or Ether transfer to the diamond, which may be undesirable if not properly guarded by access control mechanisms in other facets. +Access control for diamond upgrades (add/replace/remove facets) is typically managed by the owner. Ensure the owner address is secure. The `fallback` and `receive` functions are present but have no specific logic defined in this example, relying on the diamond proxy's default behavior.
- + diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx index df0c9aa9..769767b6 100644 --- a/website/docs/library/diamond/example/index.mdx +++ b/website/docs/library/diamond/example/index.mdx @@ -1,6 +1,7 @@ --- title: "example" description: "example components for Compose diamonds." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,7 +15,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx index 7919e942..9ce67b22 100644 --- a/website/docs/library/diamond/index.mdx +++ b/website/docs/library/diamond/index.mdx @@ -1,6 +1,7 @@ --- title: "Diamond Core" description: "Core diamond proxy functionality for ERC-2535 diamonds." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -21,35 +22,35 @@ import Icon from '@site/src/components/ui/Icon'; /> } size="medium" /> } size="medium" /> } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx index 707c45f5..8de81297 100644 --- a/website/docs/library/index.mdx +++ b/website/docs/library/index.mdx @@ -9,7 +9,7 @@ import DocSubtitle from '@site/src/components/docs/DocSubtitle'; import Icon from '@site/src/components/ui/Icon'; - Contract reference for all Compose modules and facets. + API reference for all Compose modules and facets. diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx new file mode 100644 index 00000000..4615d6fd --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx @@ -0,0 +1,140 @@ +--- +sidebar_position: 99 +title: "ERC165Facet" +description: "Implements the ERC-165 interface detection standard." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements the ERC-165 interface detection standard. + + + +- Implements EIP-165 standard for interface detection. +- Allows querying supported interfaces via `supportsInterface` function. +- Provides access to ERC-165 storage via `getStorage`. + + +## Overview + +The ERC165Facet provides standard interface detection for a Compose diamond. It allows external contracts to query which ERC-165 supported interfaces the diamond implements, enhancing composability and interoperability. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { + /** + * @notice Mapping of interface IDs to whether they are supported + */ + mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### supportsInterface + +Query if a contract implements an interface This function checks if the diamond supports the given interface ID + + +{`function supportsInterface(bytes4 _interfaceId) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC165Facet} from "@compose/diamond/facets/ERC165/IERC165Facet.sol"; + +contract Consumer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function isERC721Supported() public view returns (bool) { + // ERC721 interface ID + bytes4 interfaceId = 0x80ac58cd; + return IERC165Facet(diamondAddress).supportsInterface(interfaceId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC165Facet` is initialized with correct supported interfaces during diamond deployment. +- Use `supportsInterface` to verify compatibility before interacting with diamond functions that rely on specific interfaces. + + +## Security Considerations + + +This facet is read-only for external callers, posing minimal reentrancy risks. Ensure the `supportedInterfaces` mapping is correctly populated during initialization to prevent false positives or negatives regarding interface support. + + +
+ +
+ + diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx index 1afb8426..cd09d88a 100644 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC165Mod" -description: "Implement ERC-165 standard interface detection." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/interfaceDetection/ERC165/ERC165Mod.sol" +description: "Implements ERC-165 interface detection for diamonds." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,12 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Implement ERC-165 standard interface detection. +Implements ERC-165 interface detection for diamonds. -- Implements the ERC-165 standard for interface detection. -- Provides a dedicated storage slot for ERC-165 interface flags. -- Facilitates interoperability by allowing facets to declare supported standards. +- Provides standard ERC-165 interface detection capabilities. +- Facilitates compliance with the ERC-165 standard for diamond proxies. @@ -36,7 +35,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the necessary internal functions and storage layout to implement the ERC-165 standard for interface detection within a Compose diamond. By registering supported interfaces, facets can signal their capabilities to external callers, ensuring interoperability and adherence to standards. +The ERC165Mod provides the necessary internal functions and storage layout to support ERC-165 interface detection within a Compose diamond. By registering supported interfaces during facet initialization, diamonds can correctly report their capabilities to external callers, ensuring compliance with the ERC-165 standard. --- @@ -116,16 +115,18 @@ Register that a contract supports an interface Call this function during initial {`pragma solidity ^0.8.30; -import {LibERC165} from "@compose-protocol/diamond-contracts/contracts/modules/ERC165/LibERC165.sol"; +import {ERC165Mod, IERC165Mod} from "@compose/modules/ERC165Mod.sol"; import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; contract MyERC721Facet { - /** - * @notice Initializes the ERC721 facet, registering its supported interfaces. - */ - function initialize() external { + struct MyFacetStorage { + // ... other storage variables + } + + function initialize(MyFacetStorage storage self, address _diamondAddress) external { // Register ERC721 interface support - LibERC165.registerInterface(type(IERC721).interfaceId); + ERC165Mod.registerInterface(type(IERC721).interfaceId); + // ... other initialization logic } }`} @@ -133,19 +134,18 @@ contract MyERC721Facet { ## Best Practices -- Call `registerInterface` during facet initialization to declare supported interfaces. -- Ensure the `ERC165Mod` is correctly integrated into the diamond's storage layout. -- Rely on the diamond's access control for calling `registerInterface` if necessary, though typically done during initialization. +- Call `registerInterface` during facet initialization to declare supported ERC-165 interfaces. +- Ensure the diamond proxy implementation correctly forwards calls to the ERC165 facet. ## Integration Notes -The `ERC165Mod` utilizes a fixed storage slot for its `ERC165Storage` struct. Facets that implement ERC-165 functionality must call `LibERC165.registerInterface` during their initialization phase. This function updates the internal mapping within the `ERC165Storage` struct, making the supported interfaces discoverable via the standard ERC-165 `supportsInterface` function implemented at the diamond proxy level. +The ERC165Mod utilizes a dedicated storage slot for its `ERC165Mod.Storage` struct. The `getStorage` function uses inline assembly to ensure it always binds to the correct storage position. Facets should call `ERC165Mod.registerInterface` during their initialization to declare the interfaces they implement. This registration populates the `supportedInterfaces` mapping within the ERC165 storage.
- + diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx index 6ddd1b81..42199b63 100644 --- a/website/docs/library/interfaceDetection/ERC165/index.mdx +++ b/website/docs/library/interfaceDetection/ERC165/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-165" description: "ERC-165 components for Compose diamonds." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -12,9 +13,16 @@ import Icon from '@site/src/components/ui/Icon'; + } + size="medium" + /> } size="medium" diff --git a/website/docs/library/interfaceDetection/index.mdx b/website/docs/library/interfaceDetection/index.mdx index 65448bd8..17feecdd 100644 --- a/website/docs/library/interfaceDetection/index.mdx +++ b/website/docs/library/interfaceDetection/index.mdx @@ -1,6 +1,7 @@ --- title: "Interface Detection" description: "ERC-165 interface detection support." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx index d2e942d2..dd343485 100644 --- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC1155Facet" -description: "Manages fungible and non-fungible tokens via ERC-1155 standard." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC1155/ERC1155Facet.sol" +description: "Manages ERC-1155 fungible and non-fungible tokens." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages fungible and non-fungible tokens via ERC-1155 standard. +Manages ERC-1155 fungible and non-fungible tokens. -- Supports ERC-1155 multi-token standard. -- Provides batch transfer and balance checking capabilities. -- Manages token approvals for operators. +- Supports both fungible and non-fungible token types under the ERC-1155 standard. +- Implements batched operations (`balanceOfBatch`, `safeBatchTransferFrom`) for improved efficiency. +- Provides URI resolution for token metadata, allowing for both base URIs and token-specific URIs. ## Overview -The ERC1155Facet implements the ERC-1155 multi-token standard, enabling a diamond to manage both fungible and non-fungible tokens. It provides core functionalities for balance tracking, approvals, and transfers, facilitating a unified token management system within the diamond. +The ERC1155Facet provides a standard interface for managing multi-token standards within a Compose diamond. It supports both fungible and non-fungible tokens, enabling batched operations for transfers and balance checks, enhancing efficiency for complex interactions. --- @@ -65,28 +65,6 @@ The ERC1155Facet implements the ERC-1155 multi-token standard, enabling a diamon ## Functions -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- ### uri Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. @@ -625,23 +603,24 @@ error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; -contract ERC1155Consumer { - IERC1155Facet private immutable _erc1155Facet; +contract MyERC1155Consumer { + IERC1155Facet public erc1155Facet; constructor(address _diamondAddress) { - _erc1155Facet = IERC1155Facet(_diamondAddress); + erc1155Facet = IERC1155Facet(_diamondAddress); } - function getTokenBalance(address _account, uint256 _id) external view returns (uint256) { - return _erc1155Facet.balanceOf(_account, _id); - } + function consumeERC1155() external { + address owner = msg.sender; + uint256 tokenId = 1; + uint256 amount = 10; - function getTokenUri(uint256 _id) external view returns (string memory) { - return _erc1155Facet.uri(_id); - } + // Example: Check balance + uint256 balance = erc1155Facet.balanceOf(owner, tokenId); - function transferSingleToken(address _from, address _to, uint256 _id, uint256 _value) external { - _erc1155Facet.safeTransferFrom(_from, _to, _id, _value, ""); + // Example: Transfer tokens (assuming caller has approved operator or is owner) + // Note: This requires proper approval setup or calling from an authorized address. + // erc1155Facet.safeTransferFrom(owner, address(this), tokenId, amount, ""); } }`}
@@ -649,19 +628,19 @@ contract ERC1155Consumer { ## Best Practices -- Initialize the ERC1155Facet with the diamond proxy address before interaction. -- Ensure necessary approvals are set using `setApprovalForAll` before performing transfers on behalf of another account. -- Handle token URIs carefully, considering both base URI concatenation and the `{id}` placeholder. +- Initialize the ERC1155 facet with a unique storage slot using the diamond proxy's initializer functions. +- Implement explicit access control mechanisms for functions like `safeTransferFrom` and `safeBatchTransferFrom` if necessary, beyond the standard ERC-1155 approval patterns. +- Utilize `balanceOfBatch` and `safeBatchTransferFrom` for gas efficiency when performing operations on multiple token IDs or accounts. ## Security Considerations -Ensure sufficient balance before initiating transfers to prevent `ERC1155InsufficientBalance` errors. Validate `_from`, `_to`, and `_id` parameters to prevent unintended state changes or errors. Approvals should be managed carefully to prevent unauthorized token movements. The `safeTransferFrom` and `safeBatchTransferFrom` functions include checks to ensure the recipient contract implements the `onERC1155Received` hook if applicable. +Ensure that the `operator` address has been granted approval via `setApprovalForAll` before calling transfer functions. Validate `from`, `to`, and `id` parameters to prevent unintended transfers. Be aware of potential reentrancy if external contracts are called within custom logic that interacts with this facet.
- + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx index bf424900..8f7bc3a4 100644 --- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC1155Mod" -description: "Manage ERC-1155 tokens with minting, burning, and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC1155/ERC1155Mod.sol" +description: "Manages ERC-1155 token transfers, minting, and burning." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage ERC-1155 tokens with minting, burning, and transfers. +Manages ERC-1155 token transfers, minting, and burning. -- Full ERC-1155 token lifecycle management (minting, burning). -- Supports atomic batch operations for efficiency. -- Implements EIP-1155 safe transfer logic with receiver validation. +- Supports minting and burning of single and batched ERC-1155 token types. +- Implements safe transfer logic with receiver validation as per EIP-1155. +- Provides functionality to set base URIs and token-specific URIs for metadata. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC1155Mod provides a comprehensive interface for managing ERC-1155 tokens within a Compose diamond. It enables minting, burning, and safe transfers of both single and batch token types, adhering to EIP-1155 standards. This module is crucial for any diamond that needs to support fungible or semi-fungible token functionalities. +The ERC1155Mod provides core ERC-1155 token functionality, enabling minting, burning, and safe transfers of single and batched token types. It adheres to EIP-1155 standards, ensuring interoperability and proper handling of token receivers. This module is essential for any diamond that manages fungible or semi-fungible assets. --- @@ -561,21 +561,21 @@ error ERC1155MissingApprovalForAll(address _operator, address _owner); {`pragma solidity ^0.8.30; -import {IERC1155Mod} from "@compose/modules/ERC1155Mod.sol"; +import {IERC1155Mod} from "../interfaces/IERC1155Mod.sol"; -contract MyFacet { - address immutable diamondProxy; +contract MyERC1155Facet { + IERC1155Mod internal constant ERC1155 = IERC1155Mod(address(this)); // Replace with actual diamond proxy address - constructor(address _diamondProxy) { - diamondProxy = _diamondProxy; + function mintSomeTokens(address _to, uint256 _id, uint256 _amount) external { + ERC1155.mint(_to, _id, _amount); } - function mintErc1155Tokens(address _to, uint256 _id, uint256 _amount) external { - IERC1155Mod(diamondProxy).mint(_to, _id, _amount); + function transferMyTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + ERC1155.safeTransferFrom(_from, _to, _id, _amount, ""); } - function transferErc1155Tokens(address _from, address _to, uint256 _id, uint256 _amount) external { - IERC1155Mod(diamondProxy).safeTransferFrom(_from, _to, _id, _amount, ""); + function burnMyTokens(uint256 _id, uint256 _amount) external { + ERC1155.burn(_id, _amount); } }`} @@ -583,19 +583,19 @@ contract MyFacet { ## Best Practices -- Always validate token IDs and amounts before minting or burning to prevent unexpected state changes. -- Ensure that approvals are managed correctly before performing transfers to prevent unauthorized token movements. -- Handle `ERC1155InsufficientBalance` and `ERC1155InvalidArrayLength` errors to gracefully manage transaction failures. +- Always validate receiver addresses when minting or transferring to contracts using `ERC1155Receiver` interface checks. +- Use `safeTransferFrom` and `safeBatchTransferFrom` for all transfers to ensure receiver contract compatibility. +- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, `ERC1155InvalidReceiver`, `ERC1155InvalidSender`, and `ERC1155MissingApprovalForAll` errors appropriately. ## Integration Notes -The ERC1155Mod utilizes a dedicated storage slot within the diamond's main storage contract to manage token balances, approvals, and URIs. Facets interacting with this module will need to call its functions through the diamond proxy. The `getStorage` function can be used to directly access the module's internal storage struct, which contains mappings for balances (`_balances`), operator approvals (`_operatorApprovals`), and token URIs (`_tokenURIs`), along with the `_baseURI`. +The ERC1155Mod interacts with a predefined storage slot in the diamond for its state, including balances, approvals, and URI mappings. Facets using this module should ensure that no other facets occupy this specific storage slot to avoid conflicts. The `getStorage` function provides direct access to this storage struct, allowing other facets to read and potentially modify state if designed to do so, adhering to the diamond storage pattern.
- + diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx index f43c0cd3..4813e2ee 100644 --- a/website/docs/library/token/ERC1155/index.mdx +++ b/website/docs/library/token/ERC1155/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-1155" description: "ERC-1155 multi-token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx index 8f1d40cd..06b4b2b4 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BurnFacet" -description: "Burn ERC20 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +description: "Burn ERC-20 tokens from caller or delegated accounts." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20BurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn ERC20 tokens within a Compose diamond. +Burn ERC-20 tokens from caller or delegated accounts. -- Allows burning of ERC20 tokens from the caller's balance. -- Supports burning ERC20 tokens from another account using allowances. -- Emits `Transfer` events to the zero address upon successful burns, conforming to ERC20 standards. -- Integrates with the Compose diamond storage pattern. +- Enables reduction of total token supply. +- Supports burning from the caller's balance. +- Supports burning from another account using allowances. ## Overview -The ERC20BurnFacet provides the functionality to burn ERC20 tokens directly within a Compose diamond. It allows users to decrease their own token balances or burn tokens on behalf of others, provided they have the necessary allowances. This facet integrates with the diamond's storage pattern to manage ERC20 state. +The ERC20BurnFacet provides functionality to reduce the total supply of an ERC-20 token. It allows token holders to burn their own tokens or burn tokens on behalf of another account if they have sufficient allowance. --- @@ -64,28 +63,6 @@ The ERC20BurnFacet provides the functionality to burn ERC20 tokens directly with ## Functions -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- ### burn Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. @@ -209,46 +186,39 @@ error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _ {`pragma solidity ^0.8.30; -import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; -import {DiamondABI} from "@compose/contracts/DiamondABI.sol"; +import {DiamondLoupeFacet} from "@compose/diamond-loupe/DiamondLoupeFacet.sol"; +import {ERC20BurnFacet} from "@compose/erc20/facets/ERC20BurnFacet.sol"; -contract ERC20BurnConsumer { - address immutable DIAMOND_ADDRESS; +contract MyDiamond is DiamondLoupeFacet { + // ... other facets - constructor(address _diamondAddress) { - DIAMOND_ADDRESS = _diamondAddress; + function burn(uint256 _amount) external { + ERC20BurnFacet(address(this)).burn(_amount); } - function burnMyTokens(address _tokenAddress, uint256 _amount) external { - bytes4 selector = IERC20BurnFacet.burn.selector; - (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _tokenAddress, _amount)); - require(success, "Burn failed"); + function burnFrom(address _from, uint256 _amount) external { + ERC20BurnFacet(address(this)).burnFrom(_from, _amount); } - function burnTokensFromOthers(address _tokenAddress, address _from, uint256 _amount) external { - bytes4 selector = IERC20BurnFacet.burnFrom.selector; - (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _tokenAddress, _from, _amount)); - require(success, "Burn from failed"); - } + // ... other diamond functions }`} ## Best Practices -- Ensure the ERC20BurnFacet is correctly added to your diamond and its functions are properly routed. -- Manage token allowances carefully before calling `burnFrom` to prevent unintended token loss. -- The `getStorage` function can be used to inspect ERC20 storage directly if needed, but direct manipulation is not recommended. +- Ensure the ERC20BurnFacet is correctly added to the diamond proxy during deployment. +- Use `burn` to reduce your own token balance and `burnFrom` to reduce another account's balance when you have been granted allowance. ## Security Considerations -This facet relies on the underlying ERC20 token's transfer logic and allowance mechanism. Ensure the token contract itself is secure. The `burnFrom` function requires the caller to have sufficient allowance, and the `burn` function requires the caller to have sufficient balance. Incorrect allowances or balances will revert with `ERC20InsufficientAllowance` or `ERC20InsufficientBalance` respectively. Reentrancy is not a concern as these are state-mutating calls that do not yield control back to external contracts before state changes are finalized. +The `burnFrom` function requires that the caller has been granted sufficient allowance by the `_from` address. Ensure appropriate allowance management mechanisms are in place to prevent unintended token burning. The `Transfer` event is emitted to the zero address when tokens are burned, as per ERC-20 standards.
- + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx index 77445dbe..5a89eb5c 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20Facet" -description: "Standard ERC-20 token functionality for Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20/ERC20Facet.sol" +description: "Implements the ERC-20 token standard for Compose diamonds." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Standard ERC-20 token functionality for Compose diamonds. +Implements the ERC-20 token standard for Compose diamonds. - Full ERC-20 standard compliance. -- Standardized interface for token operations within a diamond. -- Read-only functions for token metadata and balances. -- State-changing functions for transfers and approvals. +- Operates via the diamond proxy, enabling upgradeability and composability. +- Uses a dedicated storage slot for ERC-20 state, ensuring isolation. ## Overview -The ERC20Facet provides a complete implementation of the ERC-20 token standard, enabling tokens to be managed within a Compose diamond. It exposes standard read functions and state-changing operations like transfers and approvals, ensuring interoperability and adherence to the widely adopted token standard. +The ERC20Facet provides a standard interface for fungible tokens within a Compose diamond. It handles core ERC-20 operations like transfers, approvals, and balance inquiries, enabling tokens to be seamlessly integrated and managed by the diamond proxy. --- @@ -67,28 +66,6 @@ The ERC20Facet provides a complete implementation of the ERC-20 token standard, ## Functions -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- ### name Returns the name of the token. @@ -523,25 +500,25 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Facet} from "@compose-protocol/diamond/facets/ERC20/IERC20Facet.sol"; +import {IERC20Facet} from "@compose-protocol/diamond/contracts/facets/ERC20/IERC20Facet.sol"; contract ERC20Consumer { - IERC20Facet public erc20Facet; + IERC20Facet private erc20Facet; - constructor(address _diamondAddress) { - erc20Facet = IERC20Facet(_diamondAddress); + function setERC20Facet(address _diamondProxy) public { + erc20Facet = IERC20Facet(_diamondProxy); } - function getTokenName() external view returns (string memory) { + function getTokenName() public view returns (string memory) { return erc20Facet.name(); } - function transferTokens(address _to, uint256 _amount) external { + function transferTokens(address _to, uint256 _amount) public { erc20Facet.transfer(_to, _amount); } - function approveSpender(address _spender, uint256 _amount) external { - erc20Facet.approve(_spender, _amount); + function getBalance(address _account) public view returns (uint256) { + return erc20Facet.balanceOf(_account); } }`} @@ -549,19 +526,19 @@ contract ERC20Consumer { ## Best Practices -- Initialize the ERC20Facet with the correct diamond storage slot during diamond deployment. -- Ensure the `approve` function is used before `transferFrom` to manage token allowances securely. -- Access token metadata (name, symbol, decimals) via the `external view` functions for off-chain or read-only operations. +- Initialize the ERC20Facet with the correct diamond storage slot for ERC-20 state. +- Ensure appropriate access control is configured in the diamond loupe for sensitive functions like `approve` and `transfer` if needed. +- Store the ERC20Facet's address in consumer contracts to interact with token functionalities. ## Security Considerations -This facet implements standard ERC-20 logic. Ensure proper access control is configured at the diamond level for functions that modify token balances or allowances, especially `transfer` and `approve`. Input validation for addresses and amounts is handled internally by the facet's error conditions. Reentrancy is mitigated by the diamond proxy's reentrancy guard, if implemented. +Standard ERC-20 security considerations apply, including potential reentrancy risks on `transfer` and `transferFrom` if not handled carefully by the caller. Ensure sufficient allowances are set before calling `transferFrom`. Input validation is handled by custom errors: `ERC20InsufficientBalance`, `ERC20InvalidSender`, `ERC20InvalidReceiver`, `ERC20InsufficientAllowance`, `ERC20InvalidSpender`.
- + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx index 20304e5c..66a9774c 100644 --- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20Mod" -description: "ERC-20 token standard implementation." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20/ERC20Mod.sol" +description: "ERC-20 token logic with mint, burn, and transfer capabilities." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -ERC-20 token standard implementation. +ERC-20 token logic with mint, burn, and transfer capabilities. -- Implements standard ERC-20 functions: `transfer`, `approve`, `transferFrom`. -- Supports token minting and burning operations. -- Provides access to ERC-20 specific storage via `getStorage`. +- Implements core ERC-20 functions: transfer, transferFrom, approve, mint, burn. +- Manages token balances and allowances internally. +- Provides a `getStorage` function for direct access to internal storage, useful for read-only operations or specific integrations. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the core logic and storage for the ERC-20 token standard. It enables standard token operations like transfers, approvals, minting, and burning, making it a fundamental building block for any fungible token within a Compose diamond. +This module provides the core logic for ERC-20 token functionality within a Compose diamond. It manages token balances, allowances, and total supply, enabling standard token operations like minting, burning, and transfers. Implementing this module ensures compatibility with the ERC-20 standard while leveraging the diamond's composable architecture. --- @@ -378,30 +378,30 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Mod } from "./IERC20Mod.sol"; -import { ERC20Storage } from "./ERC20Storage.sol"; +import {IERC20Mod } from "../facets/ERC20Mod.sol"; +import { IDiamondProxy } from "../diamond/IDiamondProxy.sol"; -contract ERC20Facet { - ERC20Storage private immutable _storage; +contract ERC20Consumer { + IDiamondProxy public immutable diamondProxy; - constructor(address _diamondAddress) { - // Bind storage to the diamond's storage slot - _storage = ERC20Storage(_diamondAddress); + constructor(address _diamondProxyAddress) { + diamondProxy = IDiamondProxy(_diamondProxyAddress); } - function transfer(address _to, uint256 _value) external returns (bool) { - // Call the internal transfer function from the module - return IERC20Mod(_storage).transfer(_to, _value); - } + function consumeERC20() external { + IERC20Mod erc20Facet = IERC20Mod(address(diamondProxy)); - function approve(address _spender, uint256 _value) external returns (bool) { - // Call the internal approve function from the module - return IERC20Mod(_storage).approve(_spender, _value); - } + // Example: Transfer tokens + erc20Facet.transfer(msg.sender, 100); + + // Example: Approve a spender + erc20Facet.approve(address(this), 50); + + // Example: Mint tokens (requires appropriate permissions) + // erc20Facet.mint(msg.sender, 1000); - function transferFrom(address _from, address _to, uint256 _value) external returns (bool) { - // Call the internal transferFrom function from the module - return IERC20Mod(_storage).transferFrom(_from, _to, _value); + // Example: Burn tokens + // erc20Facet.burn(50); } }`} @@ -409,19 +409,19 @@ contract ERC20Facet { ## Best Practices -- Use `transfer` and `transferFrom` for token movements, ensuring sufficient balances and allowances are checked. -- Implement `approve` carefully to manage token delegation, and be aware of potential reentrancy risks if interacting with external contracts in `transferFrom`. -- Utilize custom errors for clear revert reasons, such as `ERC20InsufficientBalance` or `ERC20InsufficientAllowance`. +- Ensure the ERC20Mod facet is correctly initialized and added to the diamond proxy. +- Implement appropriate access control for mint and burn functions if they are intended for specific roles. +- Handle potential errors such as insufficient balance, allowance, or invalid addresses. ## Integration Notes -The ERC20Mod module utilizes a fixed storage slot for its `ERC20Storage` struct. Facets interacting with this module must bind to this storage slot using `ERC20Storage(_diamondAddress)` and call the module's functions via the `IERC20Mod` interface. The module maintains invariants for token balances, allowances, and total supply, which are updated atomically during its operations. +The ERC20Mod facet interacts with a dedicated storage slot for ERC-20 state, including balances, allowances, and total supply. Facets that interact with ERC-20 tokens should call functions on the ERC20Mod facet via the diamond proxy. The `getStorage` function provides a mechanism to retrieve a pointer to the ERC-20 storage struct, enabling direct access to its state, but this should be done with caution to maintain data integrity and consider diamond upgrade scenarios.
- + diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx index fe7c1ddd..272ec9e0 100644 --- a/website/docs/library/token/ERC20/ERC20/index.mdx +++ b/website/docs/library/token/ERC20/ERC20/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-20" description: "ERC-20 fungible token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,21 +15,21 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx index 2689eeab..8aebef2e 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain token transfers for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +description: "Facilitates cross-chain token transfers for ERC20 tokens." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Facilitates cross-chain token transfers for ERC-20 tokens. +Facilitates cross-chain token transfers for ERC20 tokens. -- Enables cross-chain minting and burning of ERC-20 tokens. -- Enforces authorization for bridge operations via the `trusted-bridge` role. -- Provides internal checks for token bridging trust. +- Enables cross-chain minting of ERC20 tokens. +- Supports cross-chain burning of ERC20 tokens. +- Restricts cross-chain operations to addresses with the `trusted-bridge` role. ## Overview -The ERC20BridgeableFacet enables secure and controlled cross-chain minting and burning of ERC-20 tokens. It relies on a trusted bridge role for authorization, ensuring only authorized entities can initiate these operations. This facet provides essential functions for bridging assets across different blockchain networks. +The ERC20BridgeableFacet enables secure cross-chain minting and burning operations for ERC20 tokens within a Compose diamond. It integrates with the diamond's access control to ensure only trusted bridge operators can initiate these operations. --- @@ -76,35 +76,6 @@ The ERC20BridgeableFacet enables secure and controlled cross-chain minting and b ## Functions -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- ### crosschainMint Cross-chain mint — callable only by an address having the `trusted-bridge` role. @@ -372,25 +343,26 @@ error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _ {`pragma solidity ^0.8.30; -import {IERC20BridgeableFacet} from "@compose/diamond/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; +import {IERC20BridgeableFacet} from "@compose-protocol/diamond-contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; +import {DiamondProxy} from "@compose-protocol/diamond-contracts/diamond/DiamondProxy.sol"; -contract Deployer { - address immutable diamondAddress; - IERC20BridgeableFacet immutable erc20BridgeableFacet; +contract ExampleUser { + address internal diamondProxyAddress; - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - erc20BridgeableFacet = IERC20BridgeableFacet(diamondAddress); + constructor(address _diamondProxyAddress) { + diamondProxyAddress = _diamondProxyAddress; } - function mintCrossChain(address _token, address _to, uint256 _amount) external { - // Assumes the caller has the 'trusted-bridge' role. - erc20BridgeableFacet.crosschainMint(_token, _to, _amount); + function mintCrosschain(address _token, uint256 _amount, address _to) external { + bytes4 selector = IERC20BridgeableFacet.crosschainMint.selector; + (bool success, bytes memory data) = diamondProxyAddress.call(abi.encodeWithSelector(selector, _token, _amount, _to)); + require(success, "Crosschain mint failed"); } - function burnCrossChain(address _token, address _from, uint256 _amount) external { - // Assumes the caller has the 'trusted-bridge' role. - erc20BridgeableFacet.crosschainBurn(_token, _from, _amount); + function burnCrosschain(address _token, uint256 _amount, address _from) external { + bytes4 selector = IERC20BridgeableFacet.crosschainBurn.selector; + (bool success, bytes memory data) = diamondProxyAddress.call(abi.encodeWithSelector(selector, _token, _amount, _from)); + require(success, "Crosschain burn failed"); } }`} @@ -398,18 +370,19 @@ contract Deployer { ## Best Practices -- Ensure the 'trusted-bridge' role is correctly assigned to authorized bridge contracts or addresses. -- Use `getERC20Storage` and `getAccessControlStorage` to safely access facet storage when implementing custom logic or extensions. +- Ensure the `trusted-bridge` role is granted only to secure, audited bridge contracts or multisigs. +- Use `checkTokenBridge` internally or within other facets to verify bridge trust before executing cross-chain operations. +- Retrieve storage structs using `getERC20Storage` and `getAccessControlStorage` for accurate state access. ## Security Considerations -Cross-chain operations are sensitive. Ensure the `trusted-bridge` role is strictly managed. The `crosschainMint` and `crosschainBurn` functions are only callable by addresses holding the `trusted-bridge` role, preventing unauthorized token supply manipulation. Input validation is performed by `checkTokenBridge`. +Cross-chain operations are sensitive. Ensure that the `trusted-bridge` role is strictly managed. Input validation for token addresses, amounts, and recipient/sender addresses is critical to prevent token loss or unintended state changes. Reentrancy is not directly applicable to the cross-chain functions themselves, but downstream effects of minting/burning should be considered.
- + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx index 4fbced14..6dbb9cf6 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20BridgeableMod" -description: "Manage cross-chain ERC20 token operations and bridge access." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +description: "Manage cross-chain ERC20 token bridging." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage cross-chain ERC20 token operations and bridge access. +Manage cross-chain ERC20 token bridging. -- Enables secure cross-chain minting and burning of ERC20 tokens. -- Enforces access control for bridge operations, requiring the `trusted-bridge` role. -- Integrates with Compose's diamond storage pattern for state management. +- Restricted access for cross-chain operations via `trusted-bridge` role. +- Facilitates both burning and minting of ERC20 tokens for inter-chain transfers. +- Integrates with the diamond storage pattern for access control and ERC20 state management. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC20BridgeableMod provides functionality for cross-chain token minting and burning, ensuring secure interactions with trusted bridge operators. It leverages Compose's storage pattern to manage ERC20 and AccessControl state within the diamond. +The ERC20Bridgeable module provides functionality for cross-chain ERC20 token transfers. It enforces access control for trusted bridge operators and facilitates the burning and minting of tokens across different chains. This module is essential for interoperable token ecosystems built on Compose diamonds. --- @@ -380,20 +380,37 @@ error ERC20InvalidSender(address _sender); {`pragma solidity ^0.8.30; -import {IERC20BridgeableMod} from "./interfaces/IERC20BridgeableMod.sol"; -import {DiamondStorage} from "./DiamondStorage.sol"; +import {IERC20Bridgeable } from "@compose/modules/ERC20Bridgeable.sol"; contract MyFacet { - using DiamondStorage for DiamondStorage; + address immutable DIAMOND_FACET_CUTTER; // Assume this is set + address immutable DIAMOND_PROXY; // Assume this is set - function exampleCrosschainMint(address _token, address _to, uint256 _amount) external { - IERC20BridgeableMod bridgeableFacet = IERC20BridgeableMod(address(this)); - bridgeableFacet.crosschainMint(_token, _to, _amount); + constructor(address _diamondProxy, address _diamondFacetCutter) { + DIAMOND_PROXY = _diamondProxy; + DIAMOND_FACET_CUTTER = _diamondFacetCutter; } - function exampleCrosschainBurn(address _token, address _from, uint256 _amount) external { - IERC20BridgeableMod bridgeableFacet = IERC20BridgeableMod(address(this)); - bridgeableFacet.crosschainBurn(_token, _from, _amount); + /** + * @notice Burns tokens for cross-chain transfer. + * @param _token Address of the ERC20 token to burn. + * @param _amount Amount of tokens to burn. + * @param _to Address on the destination chain. + */ + function burnForBridge(address _token, uint256 _amount, address _to) external { + // Assuming IERC20Bridgeable is deployed and accessible via the diamond proxy + IERC20Bridgeable(DIAMOND_PROXY).crosschainBurn(_token, _amount, _to); + } + + /** + * @notice Mints tokens for cross-chain transfer. + * @param _token Address of the ERC20 token to mint. + * @param _to Address on the destination chain. + * @param _amount Amount of tokens to mint. + */ + function mintFromBridge(address _token, address _to, uint256 _amount) external { + // Assuming IERC20Bridgeable is deployed and accessible via the diamond proxy + IERC20Bridgeable(DIAMOND_PROXY).crosschainMint(_token, _to, _amount); } }`} @@ -401,19 +418,19 @@ contract MyFacet { ## Best Practices -- Ensure the `trusted-bridge` role in AccessControl is granted only to verified bridge operators to prevent unauthorized minting/burning. -- Handle `ERC20InsufficientBalance`, `ERC20InvalidBridgeAccount`, `ERC20InvalidCallerAddress`, `ERC20InvalidReciever`, and `ERC20InvalidSender` errors to gracefully manage cross-chain operation failures. -- When upgrading facets, ensure compatibility with existing ERC20 and AccessControl storage layouts to maintain data integrity. +- Ensure only addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint`. +- Validate recipient addresses on the destination chain to prevent `ERC20InvalidReciever` errors. +- Handle `ERC20InsufficientBalance` errors gracefully in your application logic when burning tokens. ## Integration Notes -This module utilizes Compose's storage pattern. `getERC20Storage` and `getAccessControlStorage` are provided as helpers to access their respective storage structs from predefined diamond storage slots. Functions like `crosschainMint` and `crosschainBurn` interact with these storage areas to validate bridge access and update token balances. Ensure that the AccessControl facet is correctly initialized with the `trusted-bridge` role before deploying or upgrading the ERC20BridgeableMod facet. +This module relies on the `AccessControl` and `ERC20` modules for its operations. The `crosschainBurn` and `crosschainMint` functions require the caller to be authorized by the `trusted-bridge` role within the `AccessControl` module. The `checkTokenBridge` internal function verifies this authorization. The module uses predefined diamond storage slots for both `AccessControlStorage` and `ERC20Storage`, accessed via inline assembly in helper functions like `getAccessControlStorage` and `getERC20Storage`. Ensure these storage slots are correctly configured and accessible by the ERC20Bridgeable facet.
- + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx index 06d24e29..d66f1fd9 100644 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-20 Bridgeable" description: "ERC-20 Bridgeable extension for ERC-20 tokens." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx index 13c14f64..efb5c61a 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20PermitFacet" -description: "Enables EIP-2612 permit functionality for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +description: "Manage ERC-20 token allowances via EIP-2612 permit signatures." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Enables EIP-2612 permit functionality for ERC-20 tokens. +Manage ERC-20 token allowances via EIP-2612 permit signatures. -- Implements EIP-2612 permit functionality for ERC-20 tokens. -- Allows token owners to grant allowances via signed messages. -- Reduces transaction overhead by enabling off-chain signature generation. -- Provides functions to retrieve nonces and the domain separator for signature construction. +- Implements EIP-2612 `permit` function for ERC-20 token approvals. +- Uses signed messages to authorize allowances, reducing on-chain transactions for users. +- Provides `nonces` and `DOMAIN_SEPARATOR` for signature verification. ## Overview -The ERC20PermitFacet integrates EIP-2612's permit functionality into a Compose diamond. It allows token owners to grant allowances to spenders via a signed message, removing the need for an explicit `approve` transaction. This enhances user experience by reducing transaction costs and improving efficiency. +The ERC20PermitFacet enables EIP-2612 compliant meta-transactions for ERC-20 token approvals. It allows users to grant allowances to spenders without directly interacting with the ERC-20 token contract, leveraging signed messages for off-chain authorization. This enhances user experience by reducing gas costs and simplifying the approval process. --- @@ -80,20 +79,6 @@ The ERC20PermitFacet integrates EIP-2612's permit functionality into a Compose d ## Functions -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- ### nonces Returns the current nonce for an owner. This value changes each time a permit is used. @@ -287,61 +272,58 @@ error ERC20InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; -import {DiamondInterface} from "@compose-protocol/diamond-interface/DiamondInterface.sol"; +import {IERC20Permit, ERC20PermitFacet} from "@compose-protocol/diamond/contracts/facets/ERC20Permit/ERC20PermitFacet.sol"; +import {IDiamondCut, DiamondLoupeFacet} from "@compose-protocol/diamond/contracts/facets/DiamondLoupe/DiamondLoupeFacet.sol"; + +contract MyDiamond is IERC20Permit { + // ... other facets -contract ERC20PermitConsumer { - DiamondInterface public diamond; + function addERC20PermitFacet(address _diamondCutFacet) external { + ERC20PermitFacet erc20PermitFacet = new ERC20PermitFacet(); + bytes32[] memory selectors = new bytes32[](5); + selectors[0] = ERC20PermitFacet.getERC20Storage.selector; + selectors[1] = ERC20PermitFacet.getStorage.selector; + selectors[2] = ERC20PermitFacet.nonces.selector; + selectors[3] = ERC20PermitFacet.DOMAIN_SEPARATOR.selector; + selectors[4] = ERC20PermitFacet.permit.selector; - constructor(address _diamondAddress) { - diamond = DiamondInterface(_diamondAddress); + IDiamondCut(_diamondCutFacet).diamondCut(new IDiamondCut.FacetCut[](0), erc20PermitFacet, selectors); } - /** - * @notice Get the current nonce for a given owner. - * @param _owner The address of the token owner. - * @return The current nonce. - */ - function getUserNonce(address _owner) external view returns (uint256) { - // The selector for nonces function from ERC20PermitFacet - bytes4 selector = bytes4(keccak256("nonces(address)")); - return abi.decode(diamond.callStatic(selector, abi.encode(_owner)), (uint256)); + // Implementation of IERC20Permit to route to the facet + function nonces(address owner) public view override returns (uint256) { + bytes4 selector = ERC20PermitFacet.nonces.selector; + (bool success, bytes memory result) = address(this).staticcall(abi.encodeWithSelector(selector, owner)); + require(success, "Nonces call failed"); + return abi.decode(result, (uint256)); } - /** - * @notice Set an allowance using EIP-2612 permit. - * @param _owner The address of the token owner. - * @param _spender The address to grant allowance to. - * @param _value The amount to allow. - * @param _deadline The permit deadline. - * @param _v The signature v component. - * @param _r The signature r component. - * @param _s The signature s component. - */ - function permitToken(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) external { - // The selector for permit function from ERC20PermitFacet - bytes4 selector = bytes4(keccak256("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")); - diamond.execute(selector, abi.encode(_owner, _spender, _value, _deadline, _v, _r, _s)); + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public override { + bytes4 selector = ERC20PermitFacet.permit.selector; + (bool success, ) = address(this).call(abi.encodeWithSelector(selector, owner, spender, value, deadline, v, r, s)); + require(success, "Permit call failed"); } + + // ... other IERC20Permit functions }`} ## Best Practices -- Ensure the ERC20PermitFacet is correctly initialized with the appropriate domain separator and chain ID during diamond deployment. -- Validate the permit deadline to prevent expired permits from being used. -- Use the `nonces` function to track the usage of permits and prevent replay attacks. +- Ensure the `ERC20PermitFacet` is added to the diamond during deployment or upgrade. +- Users will call the `permit` function via the diamond proxy, which will then route the call to the appropriate facet. +- Store the `DOMAIN_SEPARATOR` and `nonces` appropriately within the diamond's storage. ## Security Considerations -This facet implements EIP-2612, which relies on cryptographic signatures. Ensure that the signature verification process is robust and that the domain separator is correctly configured to prevent replay attacks across different contracts or chains. Input validation on `_deadline` is crucial to prevent the use of expired permits. +Users must verify the `deadline` to prevent stale permits from being used. The `DOMAIN_SEPARATOR` prevents replay attacks across different chains or contracts. Ensure the underlying ERC-20 token contract is correctly integrated and accessible for the allowance updates to take effect.
- + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx index 4137a99b..b9ebd42d 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC20PermitMod" -description: "ERC-2612 Permit logic and domain separator" -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +description: "ERC-2612 Permit and Domain Separator Logic" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -ERC-2612 Permit logic and domain separator +ERC-2612 Permit and Domain Separator Logic -- Implements ERC-2612 permit functionality for gasless allowance grants. -- Manages the domain separator for signature validation, ensuring chain and contract uniqueness. -- Provides a standardized interface for permit operations. +- Generates the domain separator required for ERC-2612 signatures. +- Validates owner signatures for token approvals. +- Allows for gasless token approvals by enabling off-chain signing. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the necessary logic and storage structures for implementing ERC-2612 permit functionality. It allows users to grant allowances via signed messages, enhancing gas efficiency and user experience. Integrating this module enables standard permit flows within your diamond. +This module provides the core logic for implementing the ERC-2612 Permit functionality. It handles domain separator generation and permit signature validation, enabling gasless token approvals for users. Integrating this module allows your diamond to support off-chain signature approvals for ERC-20 tokens. --- @@ -153,7 +153,7 @@ Validates a permit signature and sets allowance. Emits Approval event; must be e { name: "_deadline", type: "uint256", - description: "Permit\'s time deadline." + description: "Permit\'s time deadline." } ]} showRequired={false} @@ -236,43 +236,43 @@ address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, b {`pragma solidity ^0.8.30; -import {IERC20PermitMod, ERC20InvalidSpender, ERC2612InvalidSignature} from "@compose/modules/erc20-permit/src/ERC20PermitMod.sol"; +import {IERC20PermitMod} from "@compose/modules/ERC20PermitMod.sol"; contract MyERC20Facet { - using ERC20PermitMod for ERC20PermitMod.PermitStorage; + IERC20PermitMod private immutable _erc20PermitMod; - ERC20PermitMod.PermitStorage public permitStorage; - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Ensure this function is callable and correctly routed within the diamond. - // The module's permit function will validate the signature and update allowances. - permitStorage.permit(owner, spender, value, deadline, v, r, s); + constructor(address _erc20PermitModAddress) { + _erc20PermitMod = IERC20PermitMod(_erc20PermitModAddress); } - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return permitStorage.DOMAIN_SEPARATOR(); + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { + // Delegate permit validation and allowance setting to the module. + // The module requires the diamond's address to generate the correct domain separator. + // The calling facet must emit the Approval event as per ERC-20 and ERC-2612 standards. + _erc20PermitMod.permit(owner, spender, value, deadline, v, r, s, address(this)); + emit Approval(owner, spender, value); } - // Other ERC20 functions like allowance, approve, etc. + // ... other ERC-20 functions }`} ## Best Practices -- Ensure the `permit` function is correctly accessible through the diamond proxy. -- The `Approval` event must be emitted by the facet calling the module's `permit` function, not the module itself. -- Validate `deadline` to prevent stale permits. +- Ensure the `permit` function in your facet correctly emits the `Approval` event after calling the module's `permit` function. +- Verify that the `DOMAIN_SEPARATOR` is correctly calculated and utilized by the module; this is crucial for signature validity. +- Implement appropriate access control for functions that might interact with or trigger the permit logic, if necessary, though permit itself is permissionless for the owner. ## Integration Notes -This module manages its own `PermitStorage` struct. Facets integrating this module should declare a `PermitStorage` variable and use the `ERC20PermitMod` library to interact with it. The `permit` function within the module validates the signature and updates allowances. The `Approval` event, however, must be emitted by the calling facet after the module's `permit` function successfully executes. The `DOMAIN_SEPARATOR` function should be exposed by the facet to allow clients to construct valid permit messages. +The `ERC20PermitMod` library requires access to the diamond's address to correctly compute the domain separator. The `permit` function in this module validates the signature and updates the allowance, but it does not emit the `Approval` event. The facet calling the module's `permit` function must emit the `Approval` event to comply with ERC-20 and ERC-2612 standards. Storage for allowances is assumed to be managed by another facet that the `ERC20PermitMod` can interact with or that the calling facet updates based on the module's validation.
- + diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx index c546197b..7394bf87 100644 --- a/website/docs/library/token/ERC20/ERC20Permit/index.mdx +++ b/website/docs/library/token/ERC20/ERC20Permit/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-20 Permit" description: "ERC-20 Permit extension for ERC-20 tokens." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC20/index.mdx b/website/docs/library/token/ERC20/index.mdx index 2e3a8827..0bb39d2d 100644 --- a/website/docs/library/token/ERC20/index.mdx +++ b/website/docs/library/token/ERC20/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-20" description: "ERC-20 fungible token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx index 05611fc3..1804a6c7 100644 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC6909Facet" -description: "Manage ERC-6909 tokens and operator roles." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +description: "Manage ERC-6909 token balances and operator permissions." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manage ERC-6909 tokens and operator roles. +Manage ERC-6909 token balances and operator permissions. -- Implements the ERC-6909 token standard interface. -- Supports both direct transfers and transfers via approved operators. -- Manages explicit operator approvals for token IDs. +- Implements ERC-6909 standard for fungible tokens. +- Supports direct token transfers and transfers via approved spenders. +- Enables management of operator relationships for token management. ## Overview -The ERC6909Facet implements the ERC-6909 standard, enabling token transfers and operator management within a Compose diamond. It provides functions for checking balances, allowances, and managing operator approvals, facilitating composable token interactions. +This facet implements the ERC-6909 standard, enabling fungible token transfers and operator management within a Compose diamond. It provides essential functions for checking balances, allowances, and managing operator relationships, integrating seamlessly with the diamond's storage pattern. --- @@ -63,28 +63,6 @@ The ERC6909Facet implements the ERC-6909 standard, enabling token transfers and ## Functions -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- ### balanceOf Owner balance of an id. @@ -481,26 +459,26 @@ error ERC6909InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IDiamondCut} from "@compose/diamond-proxy/src/interfaces/IDiamondCut.sol"; -import {IERC6909Facet} from "./interfaces/IERC6909Facet.sol"; +import {IERC6909Facet} from "@compose/core/src/facets/ERC6909/IERC6909Facet.sol"; -contract Deployer { - address immutable diamondAddress; +contract ERC6909Consumer { + // Assume diamond interface is available + IERC6909Facet internal immutable erc6909Facet; constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; + erc6909Facet = IERC6909Facet(_diamondAddress); + } + + function getBalance(address _owner, uint256 _id) public view returns (uint256) { + return erc6909Facet.balanceOf(_owner, _id); } - function grantOperator(address _operator, uint256 _tokenId) external { - bytes4 selector = IERC6909Facet.setOperator.selector; - (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _operator, true, _tokenId)); - require(success, "Failed to grant operator"); + function transferTokens(address _to, uint256 _id, uint256 _amount) public { + erc6909Facet.transfer(_to, _id, _amount); } - function transferTokens(address _to, uint256 _tokenId, uint256 _amount) external { - bytes4 selector = IERC6909Facet.transfer.selector; - (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _to, _tokenId, _amount)); - require(success, "Failed to transfer tokens"); + function approveSpender(address _spender, uint256 _id, uint256 _amount) public { + erc6909Facet.approve(_spender, _id, _amount); } }`} @@ -508,19 +486,19 @@ contract Deployer { ## Best Practices -- Initialize operator roles and token approvals using the `setOperator` and `approve` functions respectively. -- Utilize `transfer` and `transferFrom` for token movements, ensuring sufficient balances and allowances are met. -- Access the facet's internal storage pointer via `getStorage` for advanced diagnostics or custom logic integration. +- Initialize the facet correctly within the diamond deployment process. +- Ensure adequate access control is implemented at the diamond level for sensitive functions like `setOperator` if required by your application logic. +- When upgrading, preserve storage layout compatibility as per Compose guidelines. ## Security Considerations -Ensure that `approve` and `setOperator` calls are appropriately authorized by the token owner. `transfer` and `transferFrom` will revert on insufficient balance or allowance, preventing unauthorized token movements. Input validation is handled internally by the facet to prevent invalid receiver or sender addresses. +Functions like `transfer` and `transferFrom` should have appropriate checks for sufficient balance and allowance to prevent underflows and unauthorized spending. The `setOperator` function should be guarded if its usage requires specific permissions. Input validation on `_id`, `_amount`, `_owner`, `_receiver`, `_sender`, and `_spender` is crucial to prevent unexpected behavior and potential exploits.
- + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx index 12557577..21c4c6b4 100644 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC6909Mod" -description: "Implements ERC-6909 minimal multi-token functionality." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +description: "Implements ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Implements ERC-6909 minimal multi-token functionality. +Implements ERC-6909 minimal multi-token logic. -- Supports standard ERC-6909 token operations (transfer, approve, burn, mint). -- Enables operator functionality to bypass allowance checks. -- Provides direct access to internal storage via `getStorage` for advanced interactions. +- Supports standard ERC-6909 token operations: mint, burn, transfer, approve. +- Manages operator relationships for delegated spending. +- Utilizes the diamond storage pattern for state management via `getStorage`. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the core logic and storage for the ERC-6909 standard, enabling diamonds to manage multiple token types with standard transfer, approval, and operator functionalities. It adheres to Compose's storage pattern for safe upgrades and composability. +The ERC6909Mod provides the core logic and storage structure for implementing the ERC-6909 standard. It enables managing multiple token types with standard transfer, approval, and operator functionalities within a diamond. This module ensures composability by adhering to the diamond storage pattern for its state. --- @@ -477,59 +477,58 @@ error ERC6909InvalidSpender(address _spender); {`pragma solidity ^0.8.30; -import {IERC6909Mod} from "./interfaces/IERC6909Mod.sol"; +import {IERC6909Mod} from "./IERC6909Mod.sol"; +import {ERC6909Storage} from "./ERC6909Storage.sol"; -contract MyERC6909Facet { - address immutable DIAMOND_ADDRESS; - IERC6909Mod private constant _erc6909 = IERC6909Mod(DIAMOND_ADDRESS); +contract ERC6909Facet { + // Assume STORAGE_POSITION is defined and accessible + uint256 private constant STORAGE_POSITION = 1; // Example slot - // ... other facet storage + function approve(uint256 _id, address _spender, uint256 _amount) external { + IERC6909Mod(address(this)).approve(_id, _spender, _amount); + } - constructor(address diamondAddress) { - DIAMOND_ADDRESS = diamondAddress; + function transfer(address _from, address _to, uint256 _id, uint256 _amount) external { + IERC6909Mod(address(this)).transfer(_from, _to, _id, _amount); } - /** - * @notice Transfers tokens from the caller. - * @param _id The token ID to transfer. - * @param _from The address to transfer from. - * @param _to The address to transfer to. - * @param _amount The amount to transfer. - */ - function transferFrom(uint256 _id, address _from, address _to, uint256 _amount) external { - _erc6909.transfer(_id, _from, _to, _amount); + function mint(address _to, uint256 _id, uint256 _amount) external { + IERC6909Mod(address(this)).mint(_to, _id, _amount); } - /** - * @notice Approves a spender for a token ID. - * @param _id The token ID. - * @param _spender The address to approve. - * @param _amount The amount to approve. - */ - function approve(uint256 _id, address _spender, uint256 _amount) external { - _erc6909.approve(_id, _spender, _amount); + function burn(address _from, uint256 _id, uint256 _amount) external { + IERC6909Mod(address(this)).burn(_from, _id, _amount); } - // ... other functions + function setOperator(address _operator, bool _approved) external { + IERC6909Mod(address(this)).setOperator(_operator, _approved); + } + + function getStorage() internal view returns (ERC6909Storage storage s) { + bytes32 position = keccak256(abi.encodePacked(STORAGE_POSITION)); + assembly { + s := sload(position) + } + } }`} ## Best Practices -- Ensure the `ERC6909Mod` facet is correctly initialized with the appropriate storage slot. -- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` gracefully in your facet logic. -- Be aware that operators bypass allowance checks for transfers, so manage operator roles carefully. +- Ensure the `ERC6909Storage` struct is correctly defined and placed in storage according to the `STORAGE_POSITION`. +- Handle `ERC6909InsufficientAllowance` and `ERC6909InsufficientBalance` errors appropriately in calling facets. +- Be mindful of operator status when performing transfers, as it bypasses allowance checks. ## Integration Notes -The `ERC6909Mod` relies on a dedicated storage slot for its internal `ERC6909Storage` struct. Facets interacting with this module should use the `getStorage` function to obtain a pointer to this struct. Ensure the `ERC6909Mod` facet is added to the diamond and its functions are correctly routed. The module's operations directly modify the shared diamond storage. +The ERC6909Mod relies on a specific storage slot defined by `STORAGE_POSITION` to hold its `ERC6909Storage` struct. Facets interacting with this module must ensure this slot is correctly allocated and that the `ERC6909Storage` struct is compatible. The `getStorage` function provides an assembly-based method to access this struct, maintaining the diamond's storage pattern. Any changes to the ERC6909Storage struct layout in future versions must be handled with care to maintain backward compatibility, especially regarding trailing fields.
- + diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx index 496e968e..8b036c19 100644 --- a/website/docs/library/token/ERC6909/ERC6909/index.mdx +++ b/website/docs/library/token/ERC6909/ERC6909/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-6909" description: "ERC-6909 minimal multi-token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC6909/index.mdx b/website/docs/library/token/ERC6909/index.mdx index b91f1e51..dab5e87f 100644 --- a/website/docs/library/token/ERC6909/index.mdx +++ b/website/docs/library/token/ERC6909/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-6909" description: "ERC-6909 minimal multi-token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx index 46db8dc2..710f784e 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "ERC721BurnFacet" description: "Burn ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721BurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,14 +25,14 @@ Burn ERC721 tokens within a Compose diamond. -- Allows burning of ERC721 tokens, permanently removing them from circulation. -- Emits standard `Transfer` events with `from` and `to` addresses set to the zero address, as per ERC721 standards for token destruction. -- Integrates seamlessly with the Compose diamond pattern for composable functionality. +- Burns ERC721 tokens, permanently removing them. +- Emits standard `Transfer` and `ApprovalForAll` events upon successful burning. +- Integrates seamlessly with the Compose diamond proxy pattern. ## Overview -The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens. It interacts directly with the diamond's storage to remove tokens from the ERC721 contract's state, emitting standard ERC721 events. +The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens. It integrates with the diamond proxy to manage token destruction, emitting standard ERC721 events. This facet ensures tokens are properly removed from tracking and ownership. --- @@ -64,28 +64,6 @@ The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens. ## Functions -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- ### burn Burns (destroys) a token, removing it from enumeration tracking. @@ -170,25 +148,20 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721BurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/IERC721BurnFacet.sol"; -import {DiamondProxy} from "@compose-protocol/diamond-contracts/contracts/DiamondProxy.sol"; - -contract ERC721BurnExample { - DiamondProxy public diamondProxy; +import {IERC721BurnFacet} from "@compose/core/src/facets/ERC721/IERC721BurnFacet.sol"; - // Replace with actual diamond address - address immutable DIAMOND_ADDRESS = address(0x1234567890abcdef); +contract ERC721BurnConsumer { + address immutable diamondAddress; - constructor() { - diamondProxy = DiamondProxy(DIAMOND_ADDRESS); + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; } - function burnToken(uint256 tokenId) public { + function burnToken(uint256 _tokenId) external { bytes4 selector = IERC721BurnFacet.burn.selector; - // abi.encodeWithSignature is not used here as it is a direct call to the diamond proxy - // The diamond proxy will route the call to the correct facet based on the selector - (bool success, bytes memory data) = diamondProxy.diamondCall(abi.encodeWithSelector(selector, tokenId)); - require(success, "ERC721BurnFacet: burn failed"); + // Call the burn function via the diamond proxy + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _tokenId)); + require(success, \"ERC721BurnFacet: burn failed\"); } }`} @@ -196,18 +169,18 @@ contract ERC721BurnExample { ## Best Practices -- Ensure the `ERC721BurnFacet` is correctly registered with the diamond proxy. -- Verify that the caller has the necessary approvals or ownership to burn the specified token before invoking the `burn` function. +- Ensure the ERC721BurnFacet is correctly initialized and added to the diamond proxy. +- Verify that the caller has the necessary approvals or ownership to burn the specified token before invoking the burn function. ## Security Considerations -The `burn` function must be called by an address that is the owner of the token or has been explicitly approved to burn the token. Ensure proper access control mechanisms are in place at the diamond level or within the calling contract to enforce these conditions. The facet itself does not implement additional access control beyond what is expected by the ERC721 standard. +Access control for burning is typically handled by the ERC721 standard itself (owner or approved address). Ensure that the caller possesses the correct permissions before attempting to burn a token. The `burn` function checks for token existence and sufficient approval, preventing invalid burn operations.
- + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx index 8df692c7..f159ac07 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721Facet" -description: "Manages ERC-721 token standard operations within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721/ERC721Facet.sol" +description: "Manages ERC721 token ownership, transfers, and approvals." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Facet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-721 token standard operations within a diamond. +Manages ERC721 token ownership, transfers, and approvals. -- Implements core ERC-721 functions: `name`, `symbol`, `tokenURI`, `balanceOf`, `ownerOf`, `getApproved`, `isApprovedForAll`. -- Supports token transfers via `transferFrom` and `safeTransferFrom` (with and without data). -- Manages approvals for individual tokens and for all tokens owned by an address. +- Implements core ERC721 token management functions. +- Supports token transfers with and without `data` payload for safe transfers. +- Provides mechanisms for approving individual token transfers and operator approvals. ## Overview -The ERC721Facet provides the core functionality for managing non-fungible tokens (NFTs) on-chain, adhering to the ERC-721 standard. It handles token ownership, transfers, approvals, and metadata retrieval, enabling composability for NFT marketplaces and games within a Compose diamond. +The ERC721Facet provides standard ERC721 functionality for token collections within a Compose diamond. It handles token ownership, metadata URIs, balance queries, and approval workflows, enabling composability with other facets. --- @@ -67,28 +67,6 @@ The ERC721Facet provides the core functionality for managing non-fungible tokens ## Functions -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- ### name Returns the token collection name. @@ -196,7 +174,7 @@ Returns the number of tokens owned by a given address. { name: "-", type: "uint256", - description: "The balance (number of tokens) owned by `_owner`." + description: "The balance (number of tokens) owned by `_owner`." } ]} showRequired={false} @@ -366,38 +344,6 @@ Approves or revokes permission for an operator to manage all caller's assets. showRequired={false} /> ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - --- ### transferFrom @@ -616,7 +562,7 @@ error ERC721InvalidOperator(address _operator); {`pragma solidity ^0.8.30; -import {IERC721Facet} from "@compose/contracts/src/facets/ERC721Facet.sol"; +import {IERC721Facet} from "@compose-protocol/core/contracts/facets/ERC721/IERC721Facet.sol"; contract ERC721Consumer { IERC721Facet public immutable erc721Facet; @@ -637,9 +583,9 @@ contract ERC721Consumer { return erc721Facet.ownerOf(tokenId); } - function transferToken(address from, address to, uint256 tokenId) external { - // Ensure appropriate approvals or ownership before calling - erc721Facet.transferFrom(from, to, tokenId); + function transferToken(address to, uint256 tokenId) external { + // Assumes caller is owner or approved + erc721Facet.transferFrom(msg.sender, to, tokenId); } }`} @@ -647,19 +593,19 @@ contract ERC721Consumer { ## Best Practices -- Initialize the `ERC721Facet` with correct storage slot configurations during diamond deployment. -- Ensure proper access control is implemented at the diamond level or within calling facets for sensitive operations like transfers and approvals. -- Leverage `safeTransferFrom` when interacting with potentially unknown receiver contracts to ensure ERC-721 compliance. +- Ensure the caller has the necessary approval or ownership before invoking transfer functions. +- Utilize `safeTransferFrom` when transferring to contracts to ensure receiver compatibility. +- Store the ERC721Facet's address in a read-only or immutable variable for consistent access. ## Security Considerations -Ensure that calls to `approve`, `setApprovalForAll`, `transferFrom`, and `safeTransferFrom` are guarded by appropriate access control mechanisms to prevent unauthorized actions. The internal `internalTransferFrom` function includes necessary checks for ownership and approvals, but the facet itself does not enforce external authorization beyond ERC-721 standard requirements. +Ensure proper access control checks are performed by the caller before invoking transfer functions. Use `safeTransferFrom` to prevent reentrancy issues when transferring to unknown contract addresses. Validate `to` and `from` addresses in transfer functions to prevent unexpected behavior.
- + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx index 9ae8674b..8547661b 100644 --- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -2,7 +2,7 @@ sidebar_position: 99 title: "ERC721Mod" description: "Internal logic for ERC-721 token management." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721/ERC721Mod.sol" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Mod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -25,9 +25,9 @@ Internal logic for ERC-721 token management. -- Provides internal, reusable logic for minting, burning, and transferring ERC-721 tokens. -- Leverages diamond storage pattern for efficient and consistent state management. -- Includes necessary error handling for common ERC-721 operations. +- Manages core ERC-721 state transitions (mint, burn, transfer) internally. +- Utilizes Compose's diamond storage pattern for predictable state management. +- Provides explicit error types for common ERC-721 failures. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -This module provides the core internal logic for managing ERC-721 tokens within a Compose diamond. It encapsulates essential functions like minting, burning, and transferring, ensuring consistency and efficient state management through the diamond's storage pattern. By using this module, facets can reliably integrate ERC-721 functionality without reimplementing complex state logic. +ERC721Mod provides the core internal logic for managing ERC-721 tokens within a Compose diamond. It abstracts the complexities of token minting, burning, and transfers, allowing custom facets to integrate ERC-721 functionality safely and efficiently. By leveraging Compose's storage pattern, it ensures state is managed predictably across diamond upgrades. --- @@ -314,37 +314,21 @@ error ERC721NonexistentToken(uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; +import {IERC721Mod } from "@compose/modules/erc721/ERC721Mod.sol"; contract MyERC721Facet { - struct DiamondStorage { - // ... other storage ... - IERC721Mod.ERC721Storage erc721; - } + IERC721Mod public immutable erc721Mod; - function _getERC721Storage() internal pure returns (IERC721Mod.ERC721Storage storage _erc721) { - assembly (memory-safe) { - // Slot 1 is reserved for ERC721Storage - _erc721 := sload(1) - } + constructor(address _erc721ModAddress) { + erc721Mod = IERC721Mod(_erc721ModAddress); } function mintToken(address _to, uint256 _tokenId) external { - IERC721Mod.ERC721Storage storage erc721Storage = _getERC721Storage(); - // Call the internal mint function from the module - IERC721Mod.mint(erc721Storage, _to, _tokenId); + erc721Mod.mint(_to, _tokenId); } function burnToken(uint256 _tokenId) external { - IERC721Mod.ERC721Storage storage erc721Storage = _getERC721Storage(); - // Call the internal burn function from the module - IERC721Mod.burn(erc721Storage, _tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - IERC721Mod.ERC721Storage storage erc721Storage = _getERC721Storage(); - // Call the internal transferFrom function from the module - IERC721Mod.transferFrom(erc721Storage, _from, _to, _tokenId); + erc721Mod.burn(_tokenId); } }`} @@ -352,19 +336,19 @@ contract MyERC721Facet { ## Best Practices -- Ensure the ERC721Storage struct is correctly placed in a dedicated storage slot (e.g., slot 1) and accessed via inline assembly for predictable state management. -- Implement access control within your facets to restrict who can call mint, burn, and transfer functions, as these operations modify critical token ownership data. -- Handle custom errors like `ERC721IncorrectOwner` and `ERC721NonexistentToken` gracefully in your facet logic to provide informative feedback to users. +- Ensure the ERC721Mod contract is correctly initialized and accessible by facets via its address. +- Always validate input parameters for token IDs and addresses before calling module functions. +- Handle potential `ERC721...` errors gracefully in facet logic. ## Integration Notes -The `ERC721Mod` module utilizes a dedicated storage slot (conventionally slot 1) for its `ERC721Storage` struct. Facets integrating with this module must ensure this storage slot is allocated and accessible. The `getStorage` function demonstrates how to access this storage using inline assembly. Any facet that interacts with ERC-721 state must retrieve this storage struct and pass it to the module's internal functions. Changes made via the module's functions are immediately reflected in the diamond's storage and visible to all facets. +ERC721Mod relies on a predefined storage slot for its `ERC721Storage` struct. Facets interacting with this module should be aware that operations like `mint`, `burn`, and `transferFrom` directly modify this shared storage. The `getStorage` function allows facets to read the current state of the ERC-721 storage. Any facet implementing ERC-721 functionality must respect the invariants established by this module, such as token uniqueness and ownership rules.
- + diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx index 74f7eaa2..29d42e19 100644 --- a/website/docs/library/token/ERC721/ERC721/index.mdx +++ b/website/docs/library/token/ERC721/ERC721/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-721" description: "ERC-721 non-fungible token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -21,7 +22,7 @@ import Icon from '@site/src/components/ui/Icon'; /> } size="medium" diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx index 4a4a66b2..5b26e010 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableBurnFacet" -description: "Burn tokens and manage ERC721 enumeration." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +description: "Enables burning of ERC721 tokens and maintains enumeration." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,17 +21,17 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Burn tokens and manage ERC721 enumeration. +Enables burning of ERC721 tokens and maintains enumeration. -- Burns ERC721 tokens, permanently removing them from circulation. -- Maintains enumeration order by removing burned tokens from internal tracking. +- Supports the burning of ERC721 tokens, removing them from circulation. +- Maintains the integrity of token enumeration after a burn operation, ensuring `totalSupply` and token indices remain accurate. ## Overview -This facet provides functionality to burn ERC721 tokens and updates internal enumeration tracking. It ensures that burned tokens are removed from the total supply and ownership lists, maintaining the integrity of enumerable ERC721 state. +The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens. It integrates with the diamond proxy pattern to manage token destruction while ensuring that the enumeration of remaining tokens is correctly updated. --- @@ -66,28 +66,6 @@ This facet provides functionality to burn ERC721 tokens and updates internal enu ## Functions -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- ### burn Burns (destroys) a token, removing it from enumeration tracking. @@ -184,17 +162,26 @@ error ERC721InsufficientApproval(address _operator, uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721EnumerableBurn} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/ERC721EnumerableBurn.sol"; +import {IERC721EnumerableBurnFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; + +contract MyDiamond { + // Assume diamond deployment and initialization have occurred. + // The ERC721EnumerableBurnFacet has been added and selectors are registered. -contract ExampleConsumer { - address immutable diamondProxy; + // Example of calling the burn function + function burnToken(address _to, uint256 _tokenId) external { + // Get the facet instance + IERC721EnumerableBurnFacet burnFacet = IERC721EnumerableBurnFacet(address(this)); - constructor(address _diamondProxy) { - diamondProxy = _diamondProxy; + // Call the burn function + // Note: Access control for who can burn should be implemented externally or via a separate facet. + burnFacet.burn(_to, _tokenId); } - function burnToken(uint256 _tokenId) external { - IERC721EnumerableBurn(diamondProxy).burn(_tokenId); + // Example of getting storage (for diagnostic purposes or advanced logic) + function getBurnFacetStorage() external view returns (IERC721EnumerableBurnFacet.ERC721EnumerableBurnStorage memory) { + IERC721EnumerableBurnFacet burnFacet = IERC721EnumerableBurnFacet(address(this)); + return burnFacet.getStorage(); } }`} @@ -202,18 +189,18 @@ contract ExampleConsumer { ## Best Practices -- Ensure the `ERC721EnumerableBurnFacet` is correctly initialized with the diamond proxy. -- Verify that the caller has the necessary permissions to burn the specified token, as enforced by the `burn` function's access control. +- Ensure proper access control is implemented in a separate facet or contract before allowing calls to the `burn` function to prevent unauthorized token destruction. +- Integrate this facet into your diamond's upgrade process, ensuring the `Transfer` event is handled correctly by your diamond's event aggregator if applicable. ## Security Considerations -The `burn` function requires careful access control to ensure only authorized parties can destroy tokens. The facet relies on the underlying ERC721 implementation to enforce ownership checks before burning. Ensure the `Transfer` event is correctly emitted by the facet implementation for accurate off-chain tracking. +The `burn` function should be protected by robust access control mechanisms to prevent unauthorized token destruction. Ensure that the `_to` address passed to `burn` is the current owner of the token being burned, and that the caller has the necessary approval or ownership rights. The `ERC721NonexistentToken` and `ERC721InsufficientApproval` errors provide basic validation.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx index 58cefd7d..25a07d40 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableFacet" -description: "Enumerable ERC721 token management" -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +description: "Enumerable ERC-721 token functionality" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,19 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Enumerable ERC721 token management +Enumerable ERC-721 token functionality -- Provides standard ERC721 functions (`name`, `symbol`, `ownerOf`, `balanceOf`, `approve`, `transferFrom`, `safeTransferFrom`). -- Includes enumerable features (`totalSupply`, `tokenOfOwnerByIndex`) for efficient collection querying. -- Supports metadata via `tokenURI`. -- Implements robust access control and error handling for token operations. +- Provides `totalSupply`, `balanceOf`, and `ownerOf` for standard ERC-721 queries. +- Enables efficient token enumeration with `tokenOfOwnerByIndex`. +- Supports standard ERC-721 transfer and approval functions. ## Overview -This facet provides full ERC721 functionality with enumerable extensions, enabling efficient querying of token ownership, supply, and individual token IDs by index. It integrates seamlessly into a Compose diamond, expanding its NFT capabilities. +This facet extends ERC-721 functionality by adding enumeration capabilities. It allows querying the total supply, balances, owner of specific tokens, and iterating through tokens owned by an address by index. This provides essential features for managing and interacting with collections of unique digital assets. --- @@ -71,28 +70,6 @@ This facet provides full ERC721 functionality with enumerable extensions, enabli ## Functions -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- ### name Returns the name of the token collection. @@ -297,7 +274,7 @@ Returns a token ID owned by a given address at a specific index. { name: "-", type: "uint256", - description: "The token ID owned by `_owner` at `_index`." + description: "The token ID owned by `_owner` at `_index`." } ]} showRequired={false} @@ -432,38 +409,6 @@ Approves or revokes an operator to manage all tokens of the caller. showRequired={false} /> ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - --- ### transferFrom @@ -691,27 +636,25 @@ error ERC721OutOfBoundsIndex(address _owner, uint256 _index); {`pragma solidity ^0.8.30; -import {IERC721EnumerableFacet} from "@compose-protocol/diamond/facets/ERC721/IERC721EnumerableFacet.sol"; +import {IERC721EnumerableFacet} from "@compose-protocol/diamond-contracts/facets/ERC721/ERC721EnumerableFacet.sol"; -contract MyDiamondUser { - IERC721EnumerableFacet immutable erc721Facet; +contract MyDiamondConsumer { + IERC721EnumerableFacet immutable erc721EnumerableFacet; constructor(address diamondAddress) { - // Assuming the diamond proxy is deployed and initialized - // and the ERC721EnumerableFacet is added and selectors are routed. - erc721Facet = IERC721EnumerableFacet(diamondAddress); + erc721EnumerableFacet = IERC721EnumerableFacet(diamondAddress); } - function getTokenSupply() external view returns (uint256) { - return erc721Facet.totalSupply(); + function getTokenSupply() public view returns (uint256) { + return erc721EnumerableFacet.totalSupply(); } - function getTokenOwner(uint256 tokenId) external view returns (address) { - return erc721Facet.ownerOf(tokenId); + function getOwnerOfToken(uint256 tokenId) public view returns (address) { + return erc721EnumerableFacet.ownerOf(tokenId); } - function getOwnedTokenId(address owner, uint256 index) external view returns (uint256) { - return erc721Facet.tokenOfOwnerByIndex(owner, index); + function getTokensByIndex(address owner, uint256 index) public view returns (uint256) { + return erc721EnumerableFacet.tokenOfOwnerByIndex(owner, index); } }`} @@ -719,19 +662,19 @@ contract MyDiamondUser { ## Best Practices -- Ensure the `ERC721EnumerableFacet` is correctly initialized with a unique storage slot upon deployment. -- Route `IERC721EnumerableFacet` selectors to this facet within the diamond proxy's facet address array. -- Use `internalTransferFrom` for internal state transitions to maintain data integrity. +- Utilize `tokenOfOwnerByIndex` for iterating through an owner's tokens when the exact number is unknown, after checking `balanceOf`. +- Ensure proper access control is implemented at the diamond level for functions like `transferFrom` and `safeTransferFrom`. +- When upgrading, ensure the storage layout of `ERC721EnumerableFacet` is maintained or handled according to Compose's upgrade guidelines. ## Security Considerations -This facet handles critical token ownership and transfer logic. Ensure proper access control is enforced by the diamond proxy's security layer. Input validation is performed by custom errors to prevent invalid operations. Reentrancy is mitigated through internal state management and checks before external calls where applicable. Be mindful of state coupling if other facets interact with token ownership or approvals. +The `internalTransferFrom` function is intended for internal use by other facets and should not be called directly externally. Ensure that external calls to `transferFrom` and `safeTransferFrom` are appropriately guarded by access control mechanisms at the diamond proxy level to prevent unauthorized transfers. Reentrancy is mitigated by the standard ERC-721 patterns, but careful review is advised if custom logic interacts with token transfers.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx index bbdedfc5..4227d544 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "ERC721EnumerableMod" -description: "Manages ERC721 enumerable token state and operations." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +description: "Manages enumerable ERC-721 token state within a diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC721 enumerable token state and operations. +Manages enumerable ERC-721 token state within a diamond. -- Manages the enumeration of ERC721 tokens, including token counts and order. -- Provides atomic operations for minting and burning tokens, updating enumerable state. -- Integrates seamlessly with diamond storage via its `getStorage` function for state retrieval. +- Manages token enumeration for ERC-721 compliant diamonds. +- Integrates seamlessly with diamond storage for persistent token tracking. +- Provides core logic for minting, burning, and transferring enumerable tokens. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The ERC721EnumerableMod provides essential internal logic for managing enumerable ERC721 tokens within a Compose diamond. It handles token minting, burning, and transfers while maintaining accurate counts and ownership records, crucial for compliant ERC721 implementations. +The ERC721EnumerableMod provides essential internal logic for managing ERC-721 token ownership and enumeration. It ensures tokens are correctly added and removed from internal tracking lists during minting and burning operations, enabling efficient querying of token balances and ownership within the diamond. --- @@ -297,31 +297,35 @@ error ERC721NonexistentToken(uint256 _tokenId); {`pragma solidity ^0.8.30; -import {IERC721EnumerableMod} from "@compose/modules/ERC721EnumerableMod.sol"; +import {IERC721EnumerableMod, IERC721EnumerableStorage} from "./interfaces/IERC721EnumerableMod.sol"; contract MyERC721Facet { - // Assume IERC721EnumerableMod is initialized and accessible - IERC721EnumerableMod public immutable erc721EnumerableMod; + // Assume ERC721EnumerableMod is deployed and its address is known + address immutable _erc721EnumerableModAddress; - constructor(address _erc721EnumerableMod) { - erc721EnumerableMod = IERC721EnumerableMod(_erc721EnumerableMod); + constructor(address erc721EnumerableModAddress) { + _erc721EnumerableModAddress = erc721EnumerableModAddress; } - function mintToken(address _to, uint256 _tokenId) external { - erc721EnumerableMod.mint(_to, _tokenId); - // Additional facet logic for minting + function mintToken(address to, uint256 tokenId) external { + (bool success, ) = _erc721EnumerableModAddress.call(abi.encodeWithSignature(\"mint(address,uint256)\", to, tokenId)); + require(success, \"ERC721EnumerableMod: mint failed\"); } - function burnToken(uint256 _tokenId) external { - // Assume ownership and approval checks are done here before calling burn - erc721EnumerableMod.burn(_tokenId); - // Additional facet logic for burning + function burnToken(uint256 tokenId) external { + (bool success, ) = _erc721EnumerableModAddress.call(abi.encodeWithSignature(\"burn(uint256)\", tokenId)); + require(success, \"ERC721EnumerableMod: burn failed\"); } - function transferTokenFrom(address _from, address _to, uint256 _tokenId) external { - // Assume ownership and approval checks are done here before calling transferFrom - erc721EnumerableMod.transferFrom(_from, _to, _tokenId); - // Additional facet logic for transfers + function transferToken(address from, address to, uint256 tokenId) external { + (bool success, ) = _erc721EnumerableModAddress.call(abi.encodeWithSignature(\"transferFrom(address,address,uint256)\", from, to, tokenId)); + require(success, \"ERC721EnumerableMod: transferFrom failed\"); + } + + function getEnumerableStorage() external view returns (IERC721EnumerableStorage memory) { + (bool success, bytes memory data) = _erc721EnumerableModAddress.staticcall(abi.encodeWithSignature(\"getStorage()\")); + require(success, \"ERC721EnumerableMod: getStorage failed\"); + return abi.decode(data, (IERC721EnumerableStorage)); } }`} @@ -329,19 +333,19 @@ contract MyERC721Facet { ## Best Practices -- Ensure the ERC721EnumerableMod contract is properly initialized and accessible via its address. -- Implement robust access control within your facets to enforce ownership and approval rules before calling module functions like `transferFrom` and `burn`. -- Handle module-specific errors (e.g., `ERC721NonexistentToken`, `ERC721IncorrectOwner`) appropriately in facet logic. +- Ensure proper access control is implemented in the calling facet before invoking mint, burn, or transferFrom functions to prevent unauthorized state changes. +- Always validate token existence and ownership within the facet before calling module functions that operate on specific tokens. +- Handle potential revert reasons from module functions (e.g., ERC721NonexistentToken, ERC721IncorrectOwner) gracefully in your facet logic. ## Integration Notes -The ERC721EnumerableMod utilizes a specific storage slot for its internal `ERC721EnumerableStorage` struct. Facets interacting with this module should be aware that `mint`, `burn`, and `transferFrom` directly modify this shared storage. The `getStorage` function allows facets to read this state directly using inline assembly, ensuring they have access to the most up-to-date enumerable token data. +This module interacts with diamond storage at a predefined slot to maintain its state. Facets calling this module must ensure they do not conflict with this storage slot. The module's internal storage is accessed via inline assembly in `getStorage`, making its state directly queryable by facets. Changes made via `mint`, `burn`, and `transferFrom` are persistent and reflected in the diamond's overall state.
- + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx index 41519edc..702eb1cb 100644 --- a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx +++ b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-721 Enumerable" description: "ERC-721 Enumerable extension for ERC-721 tokens." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,21 +15,21 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" /> } size="medium" diff --git a/website/docs/library/token/ERC721/index.mdx b/website/docs/library/token/ERC721/index.mdx index e3dc8b77..24a9e4be 100644 --- a/website/docs/library/token/ERC721/index.mdx +++ b/website/docs/library/token/ERC721/index.mdx @@ -1,6 +1,7 @@ --- title: "ERC-721" description: "ERC-721 non-fungible token implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx index 210058a5..6c9fd6cf 100644 --- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "RoyaltyFacet" -description: "Manages token royalties according to ERC-2981." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/Royalty/RoyaltyFacet.sol" +description: "Manages royalty information for tokens." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyFacet.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,18 +21,18 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages token royalties according to ERC-2981. +Manages royalty information for tokens. - Implements ERC-2981 `royaltyInfo` function. -- Supports token-specific and default royalty configurations. -- Calculates royalty amounts based on sale price and basis points. +- Supports token-specific royalty configurations. +- Provides a fallback to a default royalty setting. ## Overview -The RoyaltyFacet implements the ERC-2981 standard to provide royalty information for NFTs. It allows setting token-specific royalties and a default royalty, ensuring creators receive their due percentage on secondary sales. This facet integrates seamlessly with the diamond proxy pattern for efficient storage and access. +The RoyaltyFacet implements the ERC-2981 standard, enabling royalty payments on token sales. It allows for setting token-specific royalties and provides a fallback to a default royalty configuration. This facet surfaces the royalty information necessary for marketplaces and other integrators. --- @@ -77,28 +77,6 @@ The RoyaltyFacet implements the ERC-2981 standard to provide royalty information ## Functions -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- ### royaltyInfo Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. @@ -154,16 +132,16 @@ Returns royalty information for a given token and sale price. Returns token-spec import {IRoyaltyFacet} from "@compose/contracts/facets/Royalty/IRoyaltyFacet.sol"; contract RoyaltyConsumer { - address immutable _diamondAddress; + address immutable diamondAddress; + bytes4 private constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; - constructor(address diamondAddress) { - _diamondAddress = diamondAddress; + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; } - function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { - bytes4 selector = IRoyaltyFacet.royaltyInfo.selector; - (bool success, bytes memory data) = _diamondAddress.staticcall(abi.encodeWithSelector(selector, _tokenId, _salePrice)); - require(success, "RoyaltyFacet: call failed"); + function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { + (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(ROYALTY_INFO_SELECTOR, _tokenId, _salePrice)); + require(success, "RoyaltyFacet: royaltyInfo call failed"); (receiver, royaltyAmount) = abi.decode(data, (address, uint256)); return (receiver, royaltyAmount); } @@ -173,19 +151,18 @@ contract RoyaltyConsumer { ## Best Practices -- Initialize the facet with the default royalty receiver and basis points during diamond deployment. -- Use `setTokenRoyalty` to define specific royalties for individual tokens, overriding the default. -- Access royalty information via the diamond proxy to ensure correct routing and state management. +- Initialize the royalty configuration (default and token-specific) via an appropriate admin facet or deployment script. +- Ensure the `royaltyInfo` function is correctly routed to this facet by the diamond proxy. ## Security Considerations -The `royaltyInfo` function is `view`, preventing reentrancy. Access control for setting royalties should be managed at the diamond level. Ensure the `_tokenId` and `_salePrice` inputs are validated appropriately by the calling facet or contract. +The `royaltyInfo` function is read-only and does not pose reentrancy risks. Access control for setting royalty configurations should be managed by other facets responsible for administrative operations. Ensure correct routing to this facet to prevent unexpected behavior.
- + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx index a52a059d..9a0d446c 100644 --- a/website/docs/library/token/Royalty/RoyaltyMod.mdx +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "RoyaltyMod" -description: "Manages ERC-2981 royalty settings for tokens and defaults." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/token/Royalty/RoyaltyMod.sol" +description: "ERC-2981 royalty logic for NFTs." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,14 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Manages ERC-2981 royalty settings for tokens and defaults. +ERC-2981 royalty logic for NFTs. -- Implements ERC-2981 `royaltyInfo` function logic. -- Supports setting both default and token-specific royalties. -- Provides functions to delete or reset royalty configurations. +- Implements ERC-2981 standard for NFT royalties. +- Supports both default royalties applicable to all tokens and token-specific overrides. +- Provides functions to query royalty information, falling back to defaults when token-specific data is absent. +- Includes validation for royalty receivers and fee percentages to ensure correct configuration. @@ -36,7 +37,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The RoyaltyMod provides a robust implementation of the ERC-2981 royalty standard. It allows setting default royalties for all tokens and specific royalties for individual tokens, ensuring compliance and enabling revenue sharing for NFTs. This module is critical for marketplaces and secondary sales, offering a standardized way to distribute royalties. +Implements the ERC-2981 royalty standard, enabling NFTs to specify royalty payments on secondary sales. This module provides functions to set, retrieve, and manage both default and token-specific royalty information, ensuring adherence to the standard and allowing for flexible royalty configurations within a diamond. --- @@ -77,7 +78,7 @@ storage-location: erc8042:compose.erc2981 { name: "FEE_DENOMINATOR", type: "uint96", - description: " (Value: `10000`)" + description: "(Value: `10000`)" } ]} showRequired={false} @@ -299,27 +300,27 @@ error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); {`pragma solidity ^0.8.30; -import {IRoyaltyMod} from "@compose-protocol/diamond-contracts/contracts/modules/royalty/interfaces/IRoyaltyMod.sol"; +import {IRoyaltyMod} from "./interfaces/IRoyaltyMod.sol"; contract RoyaltyFacet { - IRoyaltyMod internal royaltyMod; + // Assume IRoyaltyMod is correctly imported and diamond storage is accessible. + // The actual diamond storage slot for RoyaltyMod is managed by the diamond proxy. - // Assume royaltyMod is initialized externally - - function setMyTokenRoyalty(uint256 tokenId, address payable receiver, uint16 feeNumerator, uint16 feeDenominator) external { - uint24 base = 10000; // ERC-2981 standard basis points denominator - uint16 feeBasisPoints = (feeNumerator * base) / feeDenominator; - royaltyMod.setTokenRoyalty(address(this), tokenId, receiver, feeBasisPoints); + function exampleSetDefaultRoyalty(address _receiver, uint16 _feeNumerator, uint16 _feeDenominator) external { + IRoyaltyMod royaltyMod = IRoyaltyMod(address(this)); // Assuming facet is called on diamond proxy + // The fee is calculated as (_feeNumerator / _feeDenominator) * salePrice + royaltyMod.setDefaultRoyalty(_receiver, _feeNumerator, _feeDenominator); } - function getDefaultRoyalty() external view returns (address receiver, uint16 feeBasisPoints) { - bytes32 royaltyStorageSlot = IRoyaltyMod.ROYALTY_STORAGE_SLOT; - (address defaultReceiver, uint16 defaultFee) = royaltyMod.royaltyInfo(address(this), 0, 0); // tokenId 0 for default - return (defaultReceiver, defaultFee); + function exampleSetTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeNumerator, uint16 _feeDenominator) external { + IRoyaltyMod royaltyMod = IRoyaltyMod(address(this)); + royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeNumerator, _feeDenominator); } - function deleteTokenRoyalty(uint256 tokenId) external { - royaltyMod.resetTokenRoyalty(address(this), tokenId); + function exampleGetRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { + IRoyaltyMod royaltyMod = IRoyaltyMod(address(this)); + (receiver, royaltyAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); + return (receiver, royaltyAmount); } }`} @@ -327,19 +328,19 @@ contract RoyaltyFacet { ## Best Practices -- Use `setTokenRoyalty` to assign specific royalties per token, overriding defaults. -- Call `resetTokenRoyalty` to revert a token's royalty settings to the configured default. -- Validate receiver addresses and fee percentages rigorously before setting royalties to prevent errors and ensure compliance. +- Use `setDefaultRoyalty` for broad application and `setTokenRoyalty` for specific exceptions to manage royalty configurations efficiently. +- Validate the `_receiver` address to prevent sending royalties to an invalid or unintended address, leveraging the module's built-in error checks (e.g., `ERC2981InvalidDefaultRoyaltyReceiver`). +- Be aware that calling `resetTokenRoyalty` will revert token-specific royalties, causing the `royaltyInfo` function to fall back to the default royalty settings. ## Integration Notes -The RoyaltyMod utilizes a dedicated storage slot (`ROYALTY_STORAGE_SLOT`) for its state. The `royaltyInfo` function intelligently queries token-specific royalties first, falling back to default royalties if no specific setting is found for the given `tokenId`. The `deleteDefaultRoyalty` function effectively removes the default royalty information, causing `royaltyInfo` to return `(address(0), 0)` for tokens without specific settings. The `resetTokenRoyalty` function clears token-specific settings, enabling the fallback to default royalties. +The RoyaltyMod utilizes a predefined storage slot within the diamond's storage contract to store its state, including default royalty information and token-specific royalty mappings. Facets interacting with this module should call its functions through the diamond proxy. The `getStorage` function can be used to access the module's internal storage struct directly if needed for inspection, though direct manipulation is discouraged. Changes to default or token-specific royalties are immediately reflected in subsequent calls to `royaltyInfo`.
- + diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx index 8e2d9ae8..deb4f513 100644 --- a/website/docs/library/token/Royalty/index.mdx +++ b/website/docs/library/token/Royalty/index.mdx @@ -1,6 +1,7 @@ --- title: "Royalty" description: "ERC-2981 royalty standard implementations." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,14 +15,14 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" /> } size="medium" diff --git a/website/docs/library/token/index.mdx b/website/docs/library/token/index.mdx index e18f1fe8..17b1ae16 100644 --- a/website/docs/library/token/index.mdx +++ b/website/docs/library/token/index.mdx @@ -1,6 +1,7 @@ --- title: "Token Standards" description: "Token standard implementations for Compose diamonds." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx index 27b81aee..46fb6f55 100644 --- a/website/docs/library/utils/NonReentrancyMod.mdx +++ b/website/docs/library/utils/NonReentrancyMod.mdx @@ -1,8 +1,8 @@ --- sidebar_position: 99 title: "NonReentrancyMod" -description: "Prevent reentrant calls within diamond functions." -gitSource: "https://github.com/maxnorm/Compose/blob/5736454575f67743dd05ba6cb1265bc06d0c1422/src/libraries/NonReentrancyMod.sol" +description: "Enforce non-reentrant execution within facets." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/libraries/NonReentrancyMod.sol" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; @@ -21,13 +21,13 @@ import GradientText from '@site/src/components/ui/GradientText'; import GradientButton from '@site/src/components/ui/GradientButton'; -Prevent reentrant calls within diamond functions. +Enforce non-reentrant execution within facets. -- Provides `enter()` and `exit()` functions to manage a reentrancy lock. -- Uses a simple `uint256` as a flag for the reentrancy state, minimizing storage impact. -- Can be used as a library (`using LibNonReentrancy for uint256;`) within any facet. +- Prevents reentrant function calls, enhancing security. +- Simple and explicit `enter`/`exit` pattern for easy integration. +- Utilizes a single storage slot for the reentrancy guard. @@ -36,7 +36,7 @@ This module provides internal functions for use in your custom facets. Import it ## Overview -The NonReentrancy module provides essential guards to prevent reentrant function calls, a common vulnerability in smart contracts. By integrating these functions into your facets, you ensure that sensitive operations are executed atomically and securely, maintaining the integrity of your diamond's state. +The NonReentrancyMod provides essential functions to prevent reentrant calls, ensuring the integrity and predictable execution of your diamond's facets. By managing reentrancy guards, this module safeguards against common vulnerabilities and unexpected state changes during complex transactions. --- @@ -96,34 +96,29 @@ error Reentrancy(); {`pragma solidity ^0.8.30; -import {LibNonReentrancy} from "@compose/modules/NonReentrancy/LibNonReentrancy.sol"; +import {LibNonReentrancy} from "@compose/diamond-proxy/contracts/modules/nonReentrancy/LibNonReentrancy.sol"; contract MyFacet { using LibNonReentrancy for uint256; - uint256 private _lock; + uint256 private _reentrancyGuard; - /** - * @notice Performs a protected operation. - */ - function protectedOperation() external { - // Lock the reentrancy guard before execution. - _lock.enter(); + function sensitiveOperation() external { + // Lock reentrancy before executing sensitive logic + _reentrancyGuard.enter(); - // Perform sensitive operations here... - // For example: interacting with external contracts, transferring tokens. + // ... sensitive logic ... - // Unlock the reentrancy guard after execution. - _lock.exit(); + // Unlock reentrancy after execution + _reentrancyGuard.exit(); } - /** - * @notice Example of a function that might be called internally by a protected operation. - * This function should not be callable directly if reentrancy is a concern. - */ - function _internalOperation() internal { - // This function would be called within protectedOperation. - // It does not need to manage the lock itself, as the caller does. + function _beforeAll() internal override { + // Initialize the reentrancy guard in the diamond storage + // Assuming diamond storage has a slot for the guard, e.g., \`uint256 reentrancyGuard;\` + // This initialization would typically happen once during diamond deployment or upgrade. + // For example, if \`LibNonReentrancy.storage(LibNonReentrancy.nonReentrancyStorageSlot()).reentrancyGuard\` is accessible: + // LibNonReentrancy.storage(LibNonReentrancy.nonReentrancyStorageSlot()).reentrancyGuard = 1; // Initialized state } }`} @@ -131,19 +126,18 @@ contract MyFacet { ## Best Practices -- Always pair `enter()` with `exit()` to ensure the reentrancy lock is released, even in the event of an error before `exit()` is reached (e.g., using `try/catch` if necessary or ensuring `exit()` is the last statement before returning). -- Use `delete _lock;` in the facet's initializer to reset the lock state when the facet is deployed. -- Consider the scope of the lock; a single `uint256` variable is sufficient for one facet's reentrancy protection. +- Always call `enter()` at the beginning and `exit()` at the end of any function susceptible to reentrancy. +- Ensure the reentrancy guard is correctly initialized within the diamond's storage during deployment or upgrade. ## Integration Notes -This module is designed to be integrated directly into a facet's implementation. The `LibNonReentrancy` library operates on a `uint256` variable within the facet's storage. This variable acts as the reentrancy guard flag. When a facet is deployed, this `uint256` storage slot should be initialized to `0` (its default state) to indicate that reentrancy is not currently active. The `enter` function will revert if the flag is already set (indicating reentrancy), and `exit` will reset the flag. Ensure the `uint256` variable used for the lock is declared in the facet and is not used for other purposes. +This module manages a reentrancy guard, typically stored as a `uint256` within the diamond's shared storage. The `enter()` function increments the guard, and `exit()` decrements it. A non-zero guard indicates the function is currently executing, preventing reentrant calls. Ensure the diamond's storage layout accommodates this guard, and that it is initialized to a non-zero value (e.g., 1) to signify the initial locked state before any function call. The `LibNonReentrancy.storage()` helper function is used to access the guard variable within the diamond's storage.
- + diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx index 6e81db5f..6722a23b 100644 --- a/website/docs/library/utils/index.mdx +++ b/website/docs/library/utils/index.mdx @@ -1,6 +1,7 @@ --- title: "Utilities" description: "Utility libraries and helpers for diamond development." +sidebar_class_name: hidden --- import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; @@ -14,7 +15,7 @@ import Icon from '@site/src/components/ui/Icon'; } size="medium" From b36fdb47f0f8ad8e84752b61cbb765f9d224d08a Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 19:13:34 -0500 Subject: [PATCH 65/68] update parse to extract storage location value --- .../generate-docs-utils/forge-doc-parser.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/scripts/generate-docs-utils/forge-doc-parser.js b/.github/scripts/generate-docs-utils/forge-doc-parser.js index d856b8ef..4e0ef4d0 100644 --- a/.github/scripts/generate-docs-utils/forge-doc-parser.js +++ b/.github/scripts/generate-docs-utils/forge-doc-parser.js @@ -131,6 +131,27 @@ function parseForgeDocMarkdown(content, filePath) { } } else if (currentSection === 'structs') { currentItem.definition = codeContent; + } else if (currentSection === 'stateVariables') { + // Extract type and value from constant definition + // Format: "bytes32 constant NAME = value;" or "bytes32 NAME = value;" + // Handle both with and without "constant" keyword + // Note: name is already known from the ### heading, so we just need type and value + const constantMatch = codeContent.match(/(\w+(?:\s*\d+)?)\s+(?:constant\s+)?\w+\s*=\s*(.+?)(?:\s*;)?$/); + if (constantMatch) { + currentItem.type = constantMatch[1]; + currentItem.value = constantMatch[2].trim(); + } else { + // Fallback: try to extract just the value part if it's a simple assignment + const simpleMatch = codeContent.match(/=\s*(.+?)(?:\s*;)?$/); + if (simpleMatch) { + currentItem.value = simpleMatch[1].trim(); + } + // Try to extract type from the beginning + const typeMatch = codeContent.match(/^(\w+(?:\s*\d+)?)\s+/); + if (typeMatch) { + currentItem.type = typeMatch[1]; + } + } } continue; } From dac1d44a2997d044eb12d89e19797b2e51783f16 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 19:14:40 -0500 Subject: [PATCH 66/68] remove library section --- website/docs/contracts/_category_.json | 6 - .../AccessControl/AccessControlFacet.mdx | 560 ------------- .../access/AccessControl/AccessControlMod.mdx | 443 ----------- .../access/AccessControl/_category_.json | 11 - .../AccessControlPausableFacet.mdx | 368 --------- .../AccessControlPausableMod.mdx | 386 --------- .../AccessControlPausable/_category_.json | 11 - .../AccessControlTemporalFacet.mdx | 447 ----------- .../AccessControlTemporalMod.mdx | 473 ----------- .../AccessControlTemporal/_category_.json | 11 - .../contracts/access/Owner/OwnerFacet.mdx | 210 ----- .../docs/contracts/access/Owner/OwnerMod.mdx | 273 ------- .../contracts/access/Owner/_category_.json | 11 - .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 290 ------- .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 298 ------- .../access/OwnerTwoSteps/_category_.json | 11 - website/docs/contracts/access/_category_.json | 11 - .../contracts/diamond/DiamondCutFacet.mdx | 416 ---------- .../docs/contracts/diamond/DiamondCutMod.mdx | 387 --------- .../contracts/diamond/DiamondLoupeFacet.mdx | 252 ------ website/docs/contracts/diamond/DiamondMod.mdx | 236 ------ .../docs/contracts/diamond/_category_.json | 11 - .../diamond/example/ExampleDiamond.mdx | 139 ---- .../contracts/diamond/example/_category_.json | 11 - .../interfaceDetection/ERC165/ERC165Mod.mdx | 157 ---- .../interfaceDetection/ERC165/_category_.json | 11 - .../interfaceDetection/_category_.json | 11 - .../contracts/libraries/NonReentrancyMod.mdx | 138 ---- .../docs/contracts/libraries/_category_.json | 11 - .../contracts/token/ERC1155/ERC1155Facet.mdx | 664 ---------------- .../contracts/token/ERC1155/ERC1155Mod.mdx | 605 -------------- .../contracts/token/ERC1155/_category_.json | 11 - .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 246 ------ .../token/ERC20/ERC20/ERC20Facet.mdx | 569 -------------- .../contracts/token/ERC20/ERC20/ERC20Mod.mdx | 422 ---------- .../token/ERC20/ERC20/_category_.json | 11 - .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 443 ----------- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 420 ---------- .../ERC20/ERC20Bridgeable/_category_.json | 11 - .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 339 -------- .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 291 ------- .../token/ERC20/ERC20Permit/_category_.json | 11 - .../contracts/token/ERC20/_category_.json | 11 - .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 538 ------------- .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 517 ------------ .../token/ERC6909/ERC6909/_category_.json | 11 - .../contracts/token/ERC6909/_category_.json | 11 - .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 209 ----- .../token/ERC721/ERC721/ERC721Facet.mdx | 662 ---------------- .../token/ERC721/ERC721/ERC721Mod.mdx | 358 --------- .../token/ERC721/ERC721/_category_.json | 11 - .../ERC721EnumerableBurnFacet.mdx | 225 ------ .../ERC721EnumerableFacet.mdx | 742 ------------------ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 348 -------- .../ERC721/ERC721Enumerable/_category_.json | 11 - .../contracts/token/ERC721/_category_.json | 11 - .../contracts/token/Royalty/RoyaltyFacet.mdx | 193 ----- .../contracts/token/Royalty/RoyaltyMod.mdx | 356 --------- .../contracts/token/Royalty/_category_.json | 11 - website/docs/contracts/token/_category_.json | 11 - website/docs/library/_category_.json | 10 - .../AccessControl/AccessControlFacet.mdx | 520 ------------ .../access/AccessControl/AccessControlMod.mdx | 453 ----------- .../access/AccessControl/_category_.json | 10 - .../library/access/AccessControl/index.mdx | 30 - .../AccessControlPausableFacet.mdx | 332 -------- .../AccessControlPausableMod.mdx | 377 --------- .../AccessControlPausable/_category_.json | 10 - .../access/AccessControlPausable/index.mdx | 30 - .../AccessControlTemporalFacet.mdx | 404 ---------- .../AccessControlTemporalMod.mdx | 479 ----------- .../AccessControlTemporal/_category_.json | 10 - .../access/AccessControlTemporal/index.mdx | 30 - .../docs/library/access/Owner/OwnerFacet.mdx | 189 ----- .../docs/library/access/Owner/OwnerMod.mdx | 254 ------ .../docs/library/access/Owner/_category_.json | 10 - website/docs/library/access/Owner/index.mdx | 30 - .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 246 ------ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 303 ------- .../access/OwnerTwoSteps/_category_.json | 10 - .../library/access/OwnerTwoSteps/index.mdx | 30 - website/docs/library/access/_category_.json | 10 - website/docs/library/access/index.mdx | 51 -- .../docs/library/diamond/DiamondCutFacet.mdx | 323 -------- .../docs/library/diamond/DiamondCutMod.mdx | 375 --------- .../library/diamond/DiamondInspectFacet.mdx | 151 ---- .../library/diamond/DiamondLoupeFacet.mdx | 249 ------ website/docs/library/diamond/DiamondMod.mdx | 234 ------ website/docs/library/diamond/_category_.json | 10 - .../diamond/example/ExampleDiamond.mdx | 128 --- .../library/diamond/example/_category_.json | 10 - .../docs/library/diamond/example/index.mdx | 23 - website/docs/library/diamond/index.mdx | 58 -- website/docs/library/index.mdx | 51 -- .../interfaceDetection/ERC165/ERC165Facet.mdx | 140 ---- .../interfaceDetection/ERC165/ERC165Mod.mdx | 151 ---- .../interfaceDetection/ERC165/_category_.json | 10 - .../interfaceDetection/ERC165/index.mdx | 30 - .../interfaceDetection/_category_.json | 10 - .../docs/library/interfaceDetection/index.mdx | 23 - .../library/token/ERC1155/ERC1155Facet.mdx | 646 --------------- .../docs/library/token/ERC1155/ERC1155Mod.mdx | 601 -------------- .../library/token/ERC1155/_category_.json | 10 - website/docs/library/token/ERC1155/index.mdx | 30 - .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 224 ------ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 544 ------------- .../library/token/ERC20/ERC20/ERC20Mod.mdx | 427 ---------- .../library/token/ERC20/ERC20/_category_.json | 10 - .../docs/library/token/ERC20/ERC20/index.mdx | 37 - .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 388 --------- .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 436 ---------- .../ERC20/ERC20Bridgeable/_category_.json | 10 - .../token/ERC20/ERC20Bridgeable/index.mdx | 30 - .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 329 -------- .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 278 ------- .../token/ERC20/ERC20Permit/_category_.json | 10 - .../library/token/ERC20/ERC20Permit/index.mdx | 30 - .../docs/library/token/ERC20/_category_.json | 10 - website/docs/library/token/ERC20/index.mdx | 37 - .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 504 ------------ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 534 ------------- .../token/ERC6909/ERC6909/_category_.json | 10 - .../library/token/ERC6909/ERC6909/index.mdx | 30 - .../library/token/ERC6909/_category_.json | 10 - website/docs/library/token/ERC6909/index.mdx | 23 - .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 186 ----- .../token/ERC721/ERC721/ERC721Facet.mdx | 611 -------------- .../library/token/ERC721/ERC721/ERC721Mod.mdx | 354 --------- .../token/ERC721/ERC721/_category_.json | 10 - .../library/token/ERC721/ERC721/index.mdx | 37 - .../ERC721EnumerableBurnFacet.mdx | 206 ----- .../ERC721EnumerableFacet.mdx | 680 ---------------- .../ERC721Enumerable/ERC721EnumerableMod.mdx | 351 --------- .../ERC721/ERC721Enumerable/_category_.json | 10 - .../token/ERC721/ERC721Enumerable/index.mdx | 37 - .../docs/library/token/ERC721/_category_.json | 10 - website/docs/library/token/ERC721/index.mdx | 30 - .../library/token/Royalty/RoyaltyFacet.mdx | 168 ---- .../docs/library/token/Royalty/RoyaltyMod.mdx | 346 -------- .../library/token/Royalty/_category_.json | 10 - website/docs/library/token/Royalty/index.mdx | 30 - website/docs/library/token/_category_.json | 10 - website/docs/library/token/index.mdx | 51 -- .../docs/library/utils/NonReentrancyMod.mdx | 143 ---- website/docs/library/utils/_category_.json | 10 - website/docs/library/utils/index.mdx | 23 - 146 files changed, 28194 deletions(-) delete mode 100644 website/docs/contracts/_category_.json delete mode 100644 website/docs/contracts/access/AccessControl/AccessControlFacet.mdx delete mode 100644 website/docs/contracts/access/AccessControl/AccessControlMod.mdx delete mode 100644 website/docs/contracts/access/AccessControl/_category_.json delete mode 100644 website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx delete mode 100644 website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx delete mode 100644 website/docs/contracts/access/AccessControlPausable/_category_.json delete mode 100644 website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx delete mode 100644 website/docs/contracts/access/AccessControlTemporal/_category_.json delete mode 100644 website/docs/contracts/access/Owner/OwnerFacet.mdx delete mode 100644 website/docs/contracts/access/Owner/OwnerMod.mdx delete mode 100644 website/docs/contracts/access/Owner/_category_.json delete mode 100644 website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/contracts/access/OwnerTwoSteps/_category_.json delete mode 100644 website/docs/contracts/access/_category_.json delete mode 100644 website/docs/contracts/diamond/DiamondCutFacet.mdx delete mode 100644 website/docs/contracts/diamond/DiamondCutMod.mdx delete mode 100644 website/docs/contracts/diamond/DiamondLoupeFacet.mdx delete mode 100644 website/docs/contracts/diamond/DiamondMod.mdx delete mode 100644 website/docs/contracts/diamond/_category_.json delete mode 100644 website/docs/contracts/diamond/example/ExampleDiamond.mdx delete mode 100644 website/docs/contracts/diamond/example/_category_.json delete mode 100644 website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx delete mode 100644 website/docs/contracts/interfaceDetection/ERC165/_category_.json delete mode 100644 website/docs/contracts/interfaceDetection/_category_.json delete mode 100644 website/docs/contracts/libraries/NonReentrancyMod.mdx delete mode 100644 website/docs/contracts/libraries/_category_.json delete mode 100644 website/docs/contracts/token/ERC1155/ERC1155Facet.mdx delete mode 100644 website/docs/contracts/token/ERC1155/ERC1155Mod.mdx delete mode 100644 website/docs/contracts/token/ERC1155/_category_.json delete mode 100644 website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20/_category_.json delete mode 100644 website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json delete mode 100644 website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx delete mode 100644 website/docs/contracts/token/ERC20/ERC20Permit/_category_.json delete mode 100644 website/docs/contracts/token/ERC20/_category_.json delete mode 100644 website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx delete mode 100644 website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx delete mode 100644 website/docs/contracts/token/ERC6909/ERC6909/_category_.json delete mode 100644 website/docs/contracts/token/ERC6909/_category_.json delete mode 100644 website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx delete mode 100644 website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx delete mode 100644 website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx delete mode 100644 website/docs/contracts/token/ERC721/ERC721/_category_.json delete mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx delete mode 100644 website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json delete mode 100644 website/docs/contracts/token/ERC721/_category_.json delete mode 100644 website/docs/contracts/token/Royalty/RoyaltyFacet.mdx delete mode 100644 website/docs/contracts/token/Royalty/RoyaltyMod.mdx delete mode 100644 website/docs/contracts/token/Royalty/_category_.json delete mode 100644 website/docs/contracts/token/_category_.json delete mode 100644 website/docs/library/_category_.json delete mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx delete mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx delete mode 100644 website/docs/library/access/AccessControl/_category_.json delete mode 100644 website/docs/library/access/AccessControl/index.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx delete mode 100644 website/docs/library/access/AccessControlPausable/_category_.json delete mode 100644 website/docs/library/access/AccessControlPausable/index.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx delete mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json delete mode 100644 website/docs/library/access/AccessControlTemporal/index.mdx delete mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx delete mode 100644 website/docs/library/access/Owner/OwnerMod.mdx delete mode 100644 website/docs/library/access/Owner/_category_.json delete mode 100644 website/docs/library/access/Owner/index.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx delete mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json delete mode 100644 website/docs/library/access/OwnerTwoSteps/index.mdx delete mode 100644 website/docs/library/access/_category_.json delete mode 100644 website/docs/library/access/index.mdx delete mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondCutMod.mdx delete mode 100644 website/docs/library/diamond/DiamondInspectFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx delete mode 100644 website/docs/library/diamond/DiamondMod.mdx delete mode 100644 website/docs/library/diamond/_category_.json delete mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx delete mode 100644 website/docs/library/diamond/example/_category_.json delete mode 100644 website/docs/library/diamond/example/index.mdx delete mode 100644 website/docs/library/diamond/index.mdx delete mode 100644 website/docs/library/index.mdx delete mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx delete mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx delete mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json delete mode 100644 website/docs/library/interfaceDetection/ERC165/index.mdx delete mode 100644 website/docs/library/interfaceDetection/_category_.json delete mode 100644 website/docs/library/interfaceDetection/index.mdx delete mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx delete mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx delete mode 100644 website/docs/library/token/ERC1155/_category_.json delete mode 100644 website/docs/library/token/ERC1155/index.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20/index.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json delete mode 100644 website/docs/library/token/ERC20/ERC20Permit/index.mdx delete mode 100644 website/docs/library/token/ERC20/_category_.json delete mode 100644 website/docs/library/token/ERC20/index.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx delete mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json delete mode 100644 website/docs/library/token/ERC6909/ERC6909/index.mdx delete mode 100644 website/docs/library/token/ERC6909/_category_.json delete mode 100644 website/docs/library/token/ERC6909/index.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json delete mode 100644 website/docs/library/token/ERC721/ERC721/index.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json delete mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/index.mdx delete mode 100644 website/docs/library/token/ERC721/_category_.json delete mode 100644 website/docs/library/token/ERC721/index.mdx delete mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx delete mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx delete mode 100644 website/docs/library/token/Royalty/_category_.json delete mode 100644 website/docs/library/token/Royalty/index.mdx delete mode 100644 website/docs/library/token/_category_.json delete mode 100644 website/docs/library/token/index.mdx delete mode 100644 website/docs/library/utils/NonReentrancyMod.mdx delete mode 100644 website/docs/library/utils/_category_.json delete mode 100644 website/docs/library/utils/index.mdx diff --git a/website/docs/contracts/_category_.json b/website/docs/contracts/_category_.json deleted file mode 100644 index 57623619..00000000 --- a/website/docs/contracts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Contracts", - "position": 4, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/website/docs/contracts/access/AccessControl/AccessControlFacet.mdx b/website/docs/contracts/access/AccessControl/AccessControlFacet.mdx deleted file mode 100644 index 91613086..00000000 --- a/website/docs/contracts/access/AccessControl/AccessControlFacet.mdx +++ /dev/null @@ -1,560 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Manages roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages roles and permissions within a diamond. - - - -- Role-based access control (RBAC) for granular permission management. -- Support for granting and revoking roles to individual accounts or batches. -- Ability to define and manage admin roles for other roles. - - -## Overview - -The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling administrators to grant and revoke roles to specific accounts, ensuring that only authorized users can perform sensitive operations. This facet is fundamental for securing diamond functionalities. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; -import {DiamondLoupeFacet} from "@compose/diamond/contracts/facets/DiamondLoupeFacet.sol"; -import {AccessControlFacet} from "@compose/diamond/contracts/facets/AccessControlFacet.sol"; - -contract MyDiamond is IDiamondCut { - address constant ACCESS_CONTROL_FACET_ADDRESS = address(0x123...); // Deployed AccessControlFacet address - - function upgradeAndAddAccessControl() external payable { - // ... other upgrade logic ... - FacetCut[] memory cuts = new FacetCut[](1); - cuts[0] = FacetCut({ - facetAddress: ACCESS_CONTROL_FACET_ADDRESS, - action: FacetCutAction.Add, - functionSelectors: - AccessControlFacet.getStorage.selector ^ - AccessControlFacet.hasRole.selector ^ - AccessControlFacet.requireRole.selector ^ - AccessControlFacet.getRoleAdmin.selector ^ - AccessControlFacet.setRoleAdmin.selector ^ - AccessControlFacet.grantRole.selector ^ - AccessControlFacet.revokeRole.selector ^ - AccessControlFacet.grantRoleBatch.selector ^ - AccessControlFacet.revokeRoleBatch.selector ^ - AccessControlFacet.renounceRole.selector - }); - diamondCut(cuts, address(0), ""); - } - - // Example of calling a function that requires a role - function sensitiveOperation() external { - AccessControlFacet(address(this)).requireRole( - AccessControlFacet.DEFAULT_ADMIN_ROLE(), // Example role - msg.sender - ); - // ... perform sensitive operation ... - } -} -`} - - -## Best Practices - - -- Initialize roles and grant necessary permissions during deployment or upgrade processes. -- Designate a specific role (e.g., `DEFAULT_ADMIN_ROLE`) for managing other roles and permissions. -- Use `requireRole` judiciously to protect critical functions, ensuring only authorized accounts can execute them. - - -## Security Considerations - - -Ensure that the caller has the necessary administrative role before calling functions like `setRoleAdmin`, `grantRole`, `revokeRole`, `grantRoleBatch`, and `revokeRoleBatch` to prevent unauthorized privilege escalation. The `renounceRole` function allows accounts to give up their own roles, which should be used with caution. Input validation is implicitly handled by the `requireRole` checks and role admin checks. - - -
- -
- - diff --git a/website/docs/contracts/access/AccessControl/AccessControlMod.mdx b/website/docs/contracts/access/AccessControl/AccessControlMod.mdx deleted file mode 100644 index b93780f6..00000000 --- a/website/docs/contracts/access/AccessControl/AccessControlMod.mdx +++ /dev/null @@ -1,443 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Manage roles and permissions within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage roles and permissions within a diamond. - - - -- Standardized role-based access control for diamond applications. -- Functions for granting, revoking, and checking role assignments. -- Ability to define and manage administrative roles for other roles. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControl module provides a standardized way to manage roles and permissions for accounts interacting with a Compose diamond. It ensures that sensitive functions can only be called by authorized entities, enhancing the security and integrity of the diamond's operations. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControl} from "@compose/modules/access-control/IAccessControl.sol"; - -contract AccessControlFacet { - IAccessControl internal accessControl; - - constructor(address _diamondAddress) { - accessControl = IAccessControl(_diamondAddress); - } - - function grantAdminRole(address _account) external { - bytes32 adminRole = accessControl.getStorage().adminRole; - accessControl.grantRole(adminRole, _account); - } - - function checkAccess() external view { - bytes32 someRole = accessControl.getStorage().defaultAdminRole; // Example role - requireRole(someRole, msg.sender); - } -}`} - - -## Best Practices - - -- Use `requireRole` to enforce access control checks directly within facet functions, reverting with `AccessControlUnauthorizedAccount` if unauthorized. -- Define and manage roles using `setRoleAdmin` to establish a clear hierarchy for role management. -- Grant roles judiciously; consider the principle of least privilege when assigning roles to accounts. - - -## Integration Notes - - -The AccessControl module utilizes its own storage slot within the diamond's storage layout. Facets can access this storage via the `getStorage()` function. Changes made to role assignments or role admin configurations are immediately reflected across all facets interacting with the diamond proxy. - - -
- -
- - diff --git a/website/docs/contracts/access/AccessControl/_category_.json b/website/docs/contracts/access/AccessControl/_category_.json deleted file mode 100644 index f152df3d..00000000 --- a/website/docs/contracts/access/AccessControl/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Access Control", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Role-based access control (RBAC) pattern.", - "slug": "/docs/contracts/access/AccessControl" - } -} diff --git a/website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx deleted file mode 100644 index c83da868..00000000 --- a/website/docs/contracts/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,368 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Manage roles and pausing functionality within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage roles and pausing functionality within a diamond. - - - -- Role-based pausing: Temporarily disable specific roles. -- Admin-controlled operations: Pause and unpause actions are restricted to role admins. -- Reverts on paused roles: Automatically prevents execution when a role is paused. - - -## Overview - -This facet provides robust access control and pausing capabilities for roles within a Compose diamond. It allows administrators to temporarily disable specific roles, preventing any account from utilizing functions associated with that role. This ensures critical operations can be halted safely during emergencies or maintenance. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() internal pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {Diamond} from "@compose/diamond/Diamond.sol"; -import {AccessControlPausableFacet} from "@compose/access-control/AccessControlPausableFacet.sol"; - -contract MyDiamond is Diamond { - constructor(address _diamondAdmin, address[] memory _facetCuts) Diamond(_diamondAdmin, _facetCuts) {} - - function pauseMyRole() external { - address accessControlPausableFacet = getFacetAddress(AccessControlPausableFacet.getSelector("pauseRole(bytes32)")); - (bool success, bytes memory data) = address(this).delegatecall(abi.encodeWithSelector(AccessControlPausableFacet.getSelector("pauseRole(bytes32)"), MY_ROLE_HASH)); - require(success, "Failed to pause role"); - } - - function unpauseMyRole() external { - address accessControlPausableFacet = getFacetAddress(AccessControlPausableFacet.getSelector("unpauseRole(bytes32)")); - (bool success, bytes memory data) = address(this).delegatecall(abi.encodeWithSelector(AccessControlPausableFacet.getSelector("unpauseRole(bytes32)"), MY_ROLE_HASH)); - require(success, "Failed to unpause role"); - } -} -`} - - -## Best Practices - - -- Grant and manage roles using the `AccessControl` facet before deploying this facet. -- Only the designated admin of a role can pause or unpause it. -- Integrate `requireRoleNotPaused` checks within your facet functions that depend on specific roles. - - -## Security Considerations - - -Ensure that role administration is handled securely. The `pauseRole` and `unpauseRole` functions are only callable by the admin of the specific role, mitigating unauthorized pausing. The `requireRoleNotPaused` internal function must be used diligently within other facets to prevent execution when a role is paused, avoiding unexpected behavior or vulnerabilities. - - -
- -
- - diff --git a/website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx deleted file mode 100644 index cc68b5cd..00000000 --- a/website/docs/contracts/access/AccessControlPausable/AccessControlPausableMod.mdx +++ /dev/null @@ -1,386 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Manage role-based pausing and unpausing of operations." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage role-based pausing and unpausing of operations. - - - -- Role-specific pausing: Allows individual roles to be paused independently. -- Emergency stop capability: Enables immediate cessation of operations tied to specific roles. -- Composable with Access Control: Leverages existing role structures for permissioning. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides granular control over role execution, allowing specific roles to be paused and unpaused. It integrates with the diamond's access control system to enforce pausing logic, ensuring that operations tied to a paused role cannot be executed. This is crucial for emergency stops or controlled maintenance. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { -mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausableMod} from "@compose/modules/AccessControlPausableMod.sol"; - -contract MyFacet { - IAccessControlPausableMod public constant ACCESS_CONTROL_PAUSABLE_MOD = IAccessControlPausableMod(); - - address public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); - address public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - - /** - * @notice Allows an operator to perform a sensitive action. - * @dev Reverts if the OPERATOR_ROLE is paused. - */ - function performSensitiveAction() external { - ACCESS_CONTROL_PAUSABLE_MOD.requireRoleNotPaused(OPERATOR_ROLE); - // ... perform action ... - } - - /** - * @notice Allows a pauser to pause the operator role. - * @dev Reverts if the PAUSER_ROLE is not active or if the role is already paused. - */ - function pauseOperator() external { - ACCESS_CONTROL_PAUSABLE_MOD.pauseRole(OPERATOR_ROLE); - } - - /** - * @notice Allows a pauser to unpause the operator role. - * @dev Reverts if the PAUSER_ROLE is not active or if the role is not paused. - */ - function unpauseOperator() external { - ACCESS_CONTROL_PAUSABLE_MOD.unpauseRole(OPERATOR_ROLE); - } -}`} - - -## Best Practices - - -- Integrate `requireRoleNotPaused` at the entry point of functions that should be protected by role-based pausing. -- Ensure proper role management (granting/revoking) occurs through a separate, secure mechanism before using `pauseRole` or `unpauseRole`. -- Handle `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` errors explicitly in consuming facets or client applications. - - -## Integration Notes - - -This module utilizes the standard Compose diamond storage pattern. Facets interact with it by calling its external functions. The module's state is managed within its own storage slots, distinct from other facets. The `requireRoleNotPaused` function checks both role membership and the paused status of that role, ensuring that only authorized and active roles can execute protected functions. The module emits `RolePaused` and `RoleUnpaused` events upon state changes. - - -
- -
- - diff --git a/website/docs/contracts/access/AccessControlPausable/_category_.json b/website/docs/contracts/access/AccessControlPausable/_category_.json deleted file mode 100644 index c011f583..00000000 --- a/website/docs/contracts/access/AccessControlPausable/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Pausable Access Control", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "RBAC with pause functionality.", - "slug": "/docs/contracts/access/AccessControlPausable" - } -} diff --git a/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx deleted file mode 100644 index e3878165..00000000 --- a/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,447 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments and checks for temporal access control." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages time-bound role assignments and checks for temporal access control. - - - -- Grants roles with specific expiry timestamps. -- Checks for role expiry and reverts if a role is no longer valid. -- Admin-controlled role granting and revocation with temporal constraints. - - -## Overview - -This facet extends Compose's access control by introducing time-bound role assignments. It allows administrators to grant roles with specific expiry dates and provides mechanisms to check if a role is still valid or has expired. This is crucial for scenarios requiring temporary permissions or scheduled access revocation. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() internal pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutFacet} from "@compose/diamond/facets/DiamondCutFacet.sol"; -import {AccessControlTemporalFacet} from "@compose/diamond/facets/AccessControlTemporalFacet.sol"; - -contract MyDiamond is DiamondCutFacet { - // ... constructor and other facets ... - - function grantTempAdminRole(address _account, uint64 _expiryTimestamp) external { - AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(address(this)); - bytes32 adminRole = getRoleAdmin(DEFAULT_ADMIN_ROLE); // Assuming DEFAULT_ADMIN_ROLE is defined - temporalFacet.grantRoleWithExpiry(adminRole, _account, _expiryTimestamp); - } - - function checkTempRole(address _account, bytes32 _role) external view { - AccessControlTemporalFacet temporalFacet = AccessControlTemporalFacet(address(this)); - if (temporalFacet.isRoleExpired(_role, _account)) { - revert("Role has expired"); - } - // Further checks or logic... - } -} -`} - - -## Best Practices - - -- Grant temporal roles only to trusted accounts and set appropriate expiry durations. -- Regularly audit temporal role assignments to ensure they align with current access needs. -- Utilize the `requireValidRole` function within other facets to enforce time-bound access checks. - - -## Security Considerations - - -Ensure that the caller invoking `grantRoleWithExpiry` and `revokeTemporalRole` is indeed the administrator of the role to prevent unauthorized role manipulation. The expiry timestamp should be carefully managed to avoid accidental permanent denial of access or prolonged excessive permissions. - - -
- -
- - diff --git a/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx deleted file mode 100644 index c93654dc..00000000 --- a/website/docs/contracts/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,473 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Manages role assignments with time-based expirations." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages role assignments with time-based expirations. - - - -- Grants roles with specific expiry timestamps, enabling time-limited access. -- Provides `isRoleExpired` to check the status of a role assignment. -- Reverts with specific errors (`AccessControlRoleExpired`) when expired roles are used. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module extends standard access control by allowing roles to be granted with specific expiration timestamps. It ensures that only currently valid role assignments are considered, enhancing the security and flexibility of role-based permissions within a diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { -mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/modules/AccessControlTemporalMod.sol"; - -contract MyFacet { - IAccessControlTemporalMod internal accessControlTemporalMod; - - function initialize(address _accessControlTemporalMod) public { - accessControlTemporalMod = IAccessControlTemporalMod(_accessControlTemporalMod); - } - - function grantAdminRoleTemporarily(address _user, uint64 _expiry) public { - accessControlTemporalMod.grantRoleWithExpiry(bytes32(0x0), _user, _expiry); // Assuming role 0x0 is 'admin' - } - - function checkAdmin(address _user) public view { - accessControlTemporalMod.requireValidRole(bytes32(0x0), _user); - } -}`} - - -## Best Practices - - -- Use `grantRoleWithExpiry` to set time-bound permissions, and `revokeTemporalRole` to proactively remove them early. -- Always check role validity with `requireValidRole` before executing sensitive operations. -- Be mindful of the `AccessControlRoleExpired` error, which indicates a role has expired and access should be denied. - - -## Integration Notes - - -The AccessControlTemporalMod integrates with the diamond's storage pattern. It manages its own storage, distinct from other modules. Facets interacting with this module should use the provided interface functions. The `requireValidRole` function enforces temporal validity, preventing the use of expired roles. - - -
- -
- - diff --git a/website/docs/contracts/access/AccessControlTemporal/_category_.json b/website/docs/contracts/access/AccessControlTemporal/_category_.json deleted file mode 100644 index 72b34fb4..00000000 --- a/website/docs/contracts/access/AccessControlTemporal/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Temporal Access Control", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Time-limited role-based access control.", - "slug": "/docs/contracts/access/AccessControlTemporal" - } -} diff --git a/website/docs/contracts/access/Owner/OwnerFacet.mdx b/website/docs/contracts/access/Owner/OwnerFacet.mdx deleted file mode 100644 index c275b66c..00000000 --- a/website/docs/contracts/access/Owner/OwnerFacet.mdx +++ /dev/null @@ -1,210 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "Manages contract ownership and transfer." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership and transfer. - - - -- Provides standard ERC-173 ownership functions. -- Enables programmatic ownership management via the diamond proxy. - - -## Overview - -The OwnerFacet provides essential ownership management functions for a Compose diamond. It allows for retrieving the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative functions within the diamond. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "@compose/diamond/facets/Owner/IOwnerFacet.sol"; - -contract OwnerManager { - IOwnerFacet ownerFacet; - - constructor(address _diamondAddress) { - ownerFacet = IOwnerFacet(_diamondAddress); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function transferContractOwnership(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function renounceContractOwnership() external { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Initialize the owner during diamond deployment to a trusted address. -- Use `transferOwnership` to delegate control to another address, ensuring the new owner is verified before the transfer completes. -- Only use `renounceOwnership` if explicit administrative control is no longer required. - - -## Security Considerations - - -Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Ensure the owner's private key is secured to prevent unauthorized changes. Setting the owner to `address(0)` effectively renounces ownership, making administrative functions permissionless unless other facets impose further restrictions. - - -
- -
- - diff --git a/website/docs/contracts/access/Owner/OwnerMod.mdx b/website/docs/contracts/access/Owner/OwnerMod.mdx deleted file mode 100644 index 6bd655b1..00000000 --- a/website/docs/contracts/access/Owner/OwnerMod.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "Manages ERC-173 contract ownership and transfers." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-173 contract ownership and transfers. - - - -- Manages ERC-173 compliant contract ownership. -- Provides `owner`, `transferOwnership`, and `requireOwner` functions for robust control. -- Supports ownership renouncement by transferring to `address(0)`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod provides essential ERC-173 compliant ownership management for your diamond. It defines a clear owner and provides functions to safely transfer ownership, ensuring control remains with the designated address. This module is fundamental for securing upgradeability and administrative functions within a diamond. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "@compose/contracts/modules/owner/OwnerMod.sol"; -import {IDiamondProxy} from "@compose/contracts/diamond/IDiamondProxy.sol"; - -contract MyFacet { - IDiamondProxy public diamondProxy; - IOwnerMod ownerMod; - - constructor(address _diamondProxyAddress) { - diamondProxy = IDiamondProxy(_diamondProxyAddress); - // Assumes OwnerMod is registered and accessible via the diamond proxy - ownerMod = IOwnerMod(address(this)); - } - - /** - * @notice Get the current owner of the diamond. - */ - function getCurrentOwner() external view returns (address) { - return ownerMod.owner(); - } - - /** - * @notice Transfer ownership of the diamond to a new address. - * @param _newOwner The address of the new owner. - */ - function transferDiamondOwnership(address _newOwner) external { - // Access the OwnerMod through the diamond proxy interface - ownerMod.transferOwnership(_newOwner); - } - - /** - * @notice Renounce ownership of the diamond. - */ - function renounceDiamondOwnership() external { - ownerMod.transferOwnership(address(0)); - } - - /** - * @notice Check if the caller is the owner. - */ - function assertCallerIsOwner() external view { - ownerMod.requireOwner(); - } -}`} - - -## Best Practices - - -- Ensure the OwnerMod is correctly initialized and registered within the diamond proxy during deployment. -- Always use `transferOwnership` for owner changes; do not attempt to directly manipulate storage. -- Handle the `OwnerUnauthorizedAccount` error gracefully in external calls that require ownership. - - -## Integration Notes - - -The OwnerMod stores ownership data in a dedicated storage slot. Facets can access this data via the `IOwnerMod` interface. The `getStorage` function provides a direct pointer to the internal storage struct, allowing for low-level access if necessary, though standard function calls are preferred for safety and clarity. Any facet can call `owner`, `requireOwner`, and `transferOwnership` through the diamond proxy. - - -
- -
- - diff --git a/website/docs/contracts/access/Owner/_category_.json b/website/docs/contracts/access/Owner/_category_.json deleted file mode 100644 index 52f6a99d..00000000 --- a/website/docs/contracts/access/Owner/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Owner", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Single-owner access control pattern.", - "slug": "/docs/contracts/access/Owner" - } -} diff --git a/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx deleted file mode 100644 index 3bfb7ca0..00000000 --- a/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,290 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership with a two-step transfer process. - - - -- Two-step ownership transfer for enhanced security. -- Prevents accidental ownership changes. -- Provides clear functions to view current and pending owners. - - -## Overview - -This facet implements a secure, two-step ownership transfer mechanism for Compose diamonds. It ensures that ownership changes are intentional by requiring both the current owner to initiate a transfer and the new owner to accept it, preventing accidental or unauthorized takeovers. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() internal pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "@compose-protocol/diamond-contracts/contracts/facets/owner/IOwnerTwoStepsFacet.sol"; - -contract OwnerManager { - IOwnerTwoStepsFacet public immutable ownerFacet; - - constructor(address _ownerFacetAddress) { - ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); - } - - function startOwnershipTransfer(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function acceptNewOwnership() external { - ownerFacet.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function getPendingOwner() external view returns (address) { - return ownerFacet.pendingOwner(); - } -}`} - - -## Best Practices - - -- Initialize ownership transfer by calling `transferOwnership` from the current owner. -- The new owner must call `acceptOwnership` to finalize the transfer. -- Use `owner()` and `pendingOwner()` to track ownership status. - - -## Security Considerations - - -Access control is critical: only the current owner can initiate a transfer, and only the pending owner can accept it. Ensure the `transferOwnership` function is protected by appropriate access controls if called by other contract logic. The `OwnerUnauthorizedAccount` error is emitted if non-authorized accounts attempt to call sensitive functions. - - -
- -
- - diff --git a/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx deleted file mode 100644 index 219287f9..00000000 --- a/website/docs/contracts/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,298 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "Manages contract ownership with a secure two-step transfer process." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership with a secure two-step transfer process. - - - -- Secure two-step ownership transfer to prevent accidental or unauthorized changes. -- Explicit `acceptOwnership` function for the pending owner. -- `renounceOwnership` function to permanently remove owner privileges. -- Provides `owner()` and `pendingOwner()` view functions for state inspection. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module implements a two-step ownership transfer mechanism, enhancing security by requiring explicit acceptance from the new owner. It provides essential functions for managing contract ownership, including transferring, accepting, and renouncing ownership, ensuring that administrative control changes are deliberate and auditable. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { -address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { -address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoSteps} from "../interfaces/IOwnerTwoSteps.sol"; - -contract MyFacet { - IOwnerTwoSteps private immutable _ownerTwoSteps; - - constructor(address ownerTwoStepsAddress) { - _ownerTwoSteps = IOwnerTwoSteps(ownerTwoStepsAddress); - } - - function _requireOwner() internal view { - _ownerTwoSteps.requireOwner(); - } - - function transferAdminOwnership(address _newOwner) external { - _requireOwner(); - _ownerTwoSteps.transferOwnership(_newOwner); - } - - function acceptAdminOwnership() external { - _ownerTwoSteps.acceptOwnership(); - } -}`} - - -## Best Practices - - -- Always call `transferOwnership` from the current owner and `acceptOwnership` from the pending owner. -- Use `renounceOwnership` cautiously, as it permanently relinquishes administrative control. -- Ensure the `OwnerTwoStepsMod` facet is deployed and accessible before attempting any ownership operations. - - -## Integration Notes - - -The `OwnerTwoStepsMod` stores ownership-related state in dedicated storage slots. Facets interacting with this module should be aware of the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` constants if they need to directly access or reference the storage pointers using inline assembly. The `requireOwner` function enforces access control, ensuring that only the current owner can execute sensitive administrative functions. - - -
- -
- - diff --git a/website/docs/contracts/access/OwnerTwoSteps/_category_.json b/website/docs/contracts/access/OwnerTwoSteps/_category_.json deleted file mode 100644 index 44617d2d..00000000 --- a/website/docs/contracts/access/OwnerTwoSteps/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Two-Step Owner", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "Two-step ownership transfer pattern.", - "slug": "/docs/contracts/access/OwnerTwoSteps" - } -} diff --git a/website/docs/contracts/access/_category_.json b/website/docs/contracts/access/_category_.json deleted file mode 100644 index a3b4345e..00000000 --- a/website/docs/contracts/access/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Access Control", - "position": 2, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Access control patterns for permission management in Compose diamonds.", - "slug": "/docs/contracts/access" - } -} diff --git a/website/docs/contracts/diamond/DiamondCutFacet.mdx b/website/docs/contracts/diamond/DiamondCutFacet.mdx deleted file mode 100644 index 4820e0dc..00000000 --- a/website/docs/contracts/diamond/DiamondCutFacet.mdx +++ /dev/null @@ -1,416 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Manage diamond facets and functions." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and functions. - - - -- Allows adding, replacing, and removing functions and facets from the diamond proxy. -- Supports executing an initialization function call as part of a diamond cut operation. -- Provides functions to retrieve ownership and diamond storage pointers for inspection. - - -## Overview - -The DiamondCutFacet provides essential functions for managing the diamond's upgradeability and facet composition. It allows adding, replacing, and removing functions and facets, along with direct control over diamond upgrades. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getOwnerStorage - -Returns a pointer to the owner storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getOwnerStorage() internal pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getDiamondStorage - - -{`function getDiamondStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) internal;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-proxy/contracts/facets/IDiamondCut.sol"; - -contract Deployer { - // Assume diamondProxy is an instance of the diamond proxy contract - IDiamondCut diamondProxy; - - constructor(address _diamondProxyAddress) { - diamondProxy = IDiamondCut(_diamondProxyAddress); - } - - function upgradeDiamond(address _facetAddress, bytes4[] memory _selectors, address _initAddress, bytes memory _calldata) public { - // Example: Adding a new facet - diamondProxy.diamondCut([(_facetAddress, 3, _selectors)], _initAddress, _calldata); - } - - function replaceExistingFacet(address _newFacetAddress, bytes4[] memory _selectors) public { - // Example: Replacing functions on an existing facet - diamondProxy.diamondCut([(_newFacetAddress, 2, _selectors)], address(0), ""); - } - - function removeFacetFunctions(address _facetAddress, bytes4[] memory _selectors) public { - // Example: Removing functions from a facet - diamondProxy.diamondCut([(_facetAddress, 1, _selectors)], address(0), ""); - } -}`} - - -## Best Practices - - -- Use `diamondCut` for all facet and function modifications to maintain a consistent upgrade path. -- Ensure initialization functions are correctly specified and revert appropriately when necessary to prevent state corruption. -- Carefully manage the `DiamondCutFacet`'s ownership or access control to prevent unauthorized upgrades. - - -## Security Considerations - - -The `diamondCut` function is a powerful administrative function. Access control must be strictly enforced to prevent unauthorized upgrades. Ensure that facet addresses provided are valid and that selectors are correctly mapped. Reentrancy is not a concern as the function is not designed for reentrant calls. Initialization functions should be carefully audited for security vulnerabilities. - - -
- -
- - diff --git a/website/docs/contracts/diamond/DiamondCutMod.mdx b/website/docs/contracts/diamond/DiamondCutMod.mdx deleted file mode 100644 index 4d9c1146..00000000 --- a/website/docs/contracts/diamond/DiamondCutMod.mdx +++ /dev/null @@ -1,387 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Manage diamond facets and function selectors." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and function selectors. - - - -- Atomically adds, replaces, or removes multiple facet-selector mappings in a single transaction. -- Supports optional delegatecall execution of an initialization function after the cut. -- Includes robust error handling for invalid operations and reverted initializations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCut module provides the core functionality for managing facets within a Compose diamond. It allows for the addition, replacement, and removal of functions, enabling dynamic upgrades and feature composition. This module is crucial for evolving diamond functionality while maintaining a single on-chain contract address. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * Array of all function selectors that can be called in the diamond - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut} from "@compose/diamond-contracts/contracts/modules/DiamondCutMod.sol"; -import {IFacetA} from "./IFacetA.sol"; // Example facet interface - -contract MyDiamondConsumer { - IDiamondCut public constant DIAMOND_CUT = IDiamondCut(0x1234567890abcdef1234567890abcdef12345678); // Replace with actual diamond address - - function upgradeFacetA(address _newFacetAAddress, bytes4[] memory _selectors) external { - // Define facet cut data for FacetA - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); - cuts[0] = IDiamondCut.FacetCut({ - facetAddress: _newFacetAAddress, - action: IDiamondCut.FacetCutAction.REPLACE, - selectors: _selectors - }); - - // Perform the diamond cut - // Note: In a real scenario, you would likely need to be authorized to call diamondCut. - DIAMOND_CUT.diamondCut(cuts, address(0), ""); - } - - function callFacetA(bytes4 _selector, bytes memory _calldata) external returns (bytes memory) { - // Call a function on FacetA via the diamond proxy - (bool success, bytes memory result) = address(DIAMOND_CUT).call(abi.encodeWithSelector( - _selector, // Selector for the function in FacetA - _calldata // Calldata for the function - )); - require(success, "FacetA call failed"); - return result; - } -}`} - - -## Best Practices - - -- Ensure the caller is authorized to perform diamond cut operations, as this is a privileged action. -- Carefully manage facet addresses and selectors to prevent accidental removal of essential functionality or introduction of malicious code. -- Always test diamond upgrades thoroughly in a staging environment before deploying to production. - - -## Integration Notes - - -The DiamondCut module interacts directly with the diamond proxy's storage to update the mapping of selectors to facet addresses. Any changes made via `diamondCut` are immediately reflected in the proxy's routing logic. Facets should be designed to be stateless or manage their state independently, as the diamond proxy itself does not store facet-specific data beyond the selector-to-facet mapping. Ensure that any new facets added or replaced are compatible with the diamond's overall architecture and any existing storage patterns. - - -
- -
- - diff --git a/website/docs/contracts/diamond/DiamondLoupeFacet.mdx b/website/docs/contracts/diamond/DiamondLoupeFacet.mdx deleted file mode 100644 index e89080de..00000000 --- a/website/docs/contracts/diamond/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,252 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "Query diamond facets and their associated function selectors." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Query diamond facets and their associated function selectors. - - - -- Provides read-only access to the diamond's facet registry. -- Supports querying individual facet addresses by function selector. -- Returns a structured list of all facets and their associated selectors. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, their addresses, and the specific function selectors each facet handles. This is crucial for understanding the diamond's internal structure and for building tools or dapps that interact with it. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### getStorage - - -{`function getStorage() internal pure returns (DiamondStorage storage s);`} - - ---- -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondLoupe} from "@compose/diamond/contracts/facets/DiamondLoupeFacet.sol"; - -contract DiamondUser { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getDiamondFacets() public view returns (IDiamondLoupe.Facet[] memory) { - IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); - return loupe.facets(); - } - - function getFacetAddrForSelector(bytes4 _selector) public view returns (address) { - IDiamondLoupe loupe = IDiamondLoupe(diamondAddress); - return loupe.facetAddress(_selector); - } -}`} - - -## Best Practices - - -- Integrate this facet into your diamond to enable runtime inspection of its deployed facets and their function mappings. -- Use the `facets()` function to retrieve a comprehensive list of all facets and their selectors for auditing or tooling purposes. -- Utilize `facetAddress(_selector)` to determine which facet handles a specific function call, aiding in debugging and external contract interactions. - - -## Security Considerations - - -This facet is read-only and does not modify state, making it inherently safe from reentrancy and state corruption attacks. Access control is typically managed at the diamond proxy level, ensuring only authorized entities can modify the facet registry itself. - - -
- -
- - diff --git a/website/docs/contracts/diamond/DiamondMod.mdx b/website/docs/contracts/diamond/DiamondMod.mdx deleted file mode 100644 index 4a18a1f2..00000000 --- a/website/docs/contracts/diamond/DiamondMod.mdx +++ /dev/null @@ -1,236 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Manages diamond facets and fallback logic" -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages diamond facets and fallback logic - - - -- Manages facet registration and function selector mapping. -- Implements the diamond fallback mechanism for routing calls to appropriate facets. -- Supports adding multiple facets and their functions in a single operation during deployment. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondMod provides core logic for managing facets within a Compose diamond. It handles adding facets during deployment and routing external calls to the correct facet, ensuring composability and proper execution of diamond functions. This module is fundamental for extending diamond functionality. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { -mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; -/** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ -bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { -address facet; -uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { -address facetAddress; -FacetCutAction action; -bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondMod} from "@compose/contracts/diamond/interfaces/IDiamondMod.sol"; - -contract MyFacet { - IDiamondMod internal diamondMod; - - constructor(address _diamondMod) { - diamondMod = IDiamondMod(_diamondMod); - } - - /** - * @notice Example of calling a function through the diamond proxy. - */ - function callFacetFunction(bytes4 _selector, address _facetAddress) external returns (bytes memory) { - // In a real scenario, you would likely not pass the facet address directly - // but rather let diamondFallback resolve it. This is for demonstration. - return diamondMod.diamondFallback(_selector, _facetAddress, ""); - } -}`} - - -## Best Practices - - -- Facet additions are restricted to the initial diamond deployment phase. -- Ensure correct function selector mapping when adding facets to prevent routing issues. -- Handle potential errors such as `FunctionNotFound` or `CannotAddFunctionToDiamondThatAlreadyExists`. - - -## Integration Notes - - -DiamondMod interacts directly with the diamond proxy's storage to maintain mappings of function selectors to facet addresses. The `addFacets` function is designed to be called only during the initial deployment of the diamond. The `diamondFallback` function is crucial for the diamond proxy's operation, as it resolves incoming function calls to their respective facets. Any changes to facet mappings managed by this module are immediately reflected in the diamond's behavior. - - -
- -
- - diff --git a/website/docs/contracts/diamond/_category_.json b/website/docs/contracts/diamond/_category_.json deleted file mode 100644 index dce4984f..00000000 --- a/website/docs/contracts/diamond/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Diamond Core", - "position": 1, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Core diamond proxy functionality for ERC-2535 diamonds.", - "slug": "/docs/contracts/diamond" - } -} diff --git a/website/docs/contracts/diamond/example/ExampleDiamond.mdx b/website/docs/contracts/diamond/example/ExampleDiamond.mdx deleted file mode 100644 index 49700de8..00000000 --- a/website/docs/contracts/diamond/example/ExampleDiamond.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Example Diamond contract for Compose diamonds" -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Example Diamond contract for Compose diamonds - - - -- Supports initialization of the diamond with multiple facets. -- Manages the registration of function selectors to facet addresses for routing. -- Establishes the initial owner of the diamond contract. - - -## Overview - -This contract serves as a foundational example for Compose diamonds. It demonstrates the basic structure and initialization process for adding facets to a diamond proxy. Its primary purpose is to set up the diamond's initial state, including registering facets and assigning ownership. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondInit, FacetCut, FacetCutAction} from "@compose/diamond/contracts/DiamondInit.sol"; - -contract ExampleDiamondInit is DiamondInit { - function init(FacetCut[] memory _diamondCuts) public payable { - // Call the parent init function to process facet cuts - processCuts(_diamondCuts); - } - - // Example of how you might define cuts for initialization - function getExampleCuts() public pure returns (FacetCut[] memory) { - // Assume MyFacet and AnotherFacet are deployed and have their addresses - // and selectors defined. - FacetCut[] memory cuts = new FacetCut[](2); - - // Example cut for MyFacet - cuts[0] = FacetCut({ - facetAddress: address(0x123...\/\* MyFacet address *\/ ), - action: FacetCutAction.Add, - functionSelectors: new bytes4[](0) // Populate with actual selectors - }); - - // Example cut for AnotherFacet - cuts[1] = FacetCut({ - facetAddress: address(0x456...\/\* AnotherFacet address *\/ ), - action: FacetCutAction.Add, - functionSelectors: new bytes4[](0) // Populate with actual selectors - }); - - return cuts; - } -}`} - - -## Best Practices - - -- Initialize the diamond with all necessary facets and their selectors during deployment. -- Ensure the `owner` is set correctly to manage the diamond's upgradeability. -- Use explicit initializer functions rather than constructors for deployment flows. - - -## Security Considerations - - -The constructor grants significant control over the diamond's initial state and ownership. Ensure that the provided facet addresses are trusted and that the function selectors are correctly mapped to prevent unintended behavior. Access control for subsequent upgrades is managed by the owner. - - -
- -
- - diff --git a/website/docs/contracts/diamond/example/_category_.json b/website/docs/contracts/diamond/example/_category_.json deleted file mode 100644 index b3817d91..00000000 --- a/website/docs/contracts/diamond/example/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "example", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "example components for Compose diamonds.", - "slug": "/docs/contracts/diamond/example" - } -} diff --git a/website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx deleted file mode 100644 index 2ecf6039..00000000 --- a/website/docs/contracts/interfaceDetection/ERC165/ERC165Mod.mdx +++ /dev/null @@ -1,157 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "Implement ERC-165 interface detection for diamond facets." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implement ERC-165 interface detection for diamond facets. - - - -- Standard ERC-165 interface detection. -- Internal library for easy integration into facets. -- Minimal storage footprint. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod provides the necessary storage and internal functions to implement the ERC-165 standard for interface detection within a diamond proxy. This allows other contracts to query which interfaces a diamond facet supports, enhancing composability and interoperability. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { -/* - * @notice Mapping of interface IDs to whether they are supported - */ -mapping(bytes4 => bool) supportedInterfaces; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibERC165, IERC165} from "@compose/modules/erc165/LibERC165.sol"; - -contract MyERC721Facet { - struct Storage { - LibERC165.ERC165Storage erc165Storage; - // other facet storage variables - } - - function initialize(Storage storage self) external { - LibERC165.registerInterface(self.erc165Storage, type(IERC721).interfaceId); - } - - function supportsInterface(bytes4 interfaceId) external view override returns (bool) { - // Access storage via diamond storage layout - Storage storage self = Storage(address(this).code.bytes.offset(0)); - return LibERC165.supportsInterface(self.erc165Storage, interfaceId); - } -}`} - - -## Best Practices - - -- Call `registerInterface` during facet initialization to declare supported interfaces. -- Ensure the `ERC165Storage` struct is included in your facet's storage layout. -- Implement `supportsInterface` in facets that declare support for interfaces. - - -## Integration Notes - - -The ERC165Mod requires an `ERC165Storage` struct within each facet that implements ERC-165. This struct should be placed at the beginning of the facet's storage layout to ensure consistent slot allocation across upgrades. The `getStorage` function is used internally to bind to the correct storage slot. Facets should implement the `supportsInterface` function, calling `LibERC165.supportsInterface` to check registered interfaces. - - -
- -
- - diff --git a/website/docs/contracts/interfaceDetection/ERC165/_category_.json b/website/docs/contracts/interfaceDetection/ERC165/_category_.json deleted file mode 100644 index 2fbfe2da..00000000 --- a/website/docs/contracts/interfaceDetection/ERC165/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-165", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-165 components for Compose diamonds.", - "slug": "/docs/contracts/interfaceDetection/ERC165" - } -} diff --git a/website/docs/contracts/interfaceDetection/_category_.json b/website/docs/contracts/interfaceDetection/_category_.json deleted file mode 100644 index 7fe0636c..00000000 --- a/website/docs/contracts/interfaceDetection/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Interface Detection", - "position": 5, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "ERC-165 interface detection support.", - "slug": "/docs/contracts/interfaceDetection" - } -} diff --git a/website/docs/contracts/libraries/NonReentrancyMod.mdx b/website/docs/contracts/libraries/NonReentrancyMod.mdx deleted file mode 100644 index 6f010dd4..00000000 --- a/website/docs/contracts/libraries/NonReentrancyMod.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "Prevent reentrant calls within diamond functions." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Prevent reentrant calls within diamond functions. - - - -- Atomic execution guarantee: Prevents reentrant calls, ensuring functions complete without interruption. -- Simple integration: Utilizes a straightforward `enter`/`exit` pattern for easy adoption. -- Gas efficiency: Designed to minimize gas overhead associated with reentrancy protection. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod module provides essential functions to prevent reentrant function calls, safeguarding against reentrancy attacks. By integrating this module, developers ensure that sensitive operations within a diamond are executed atomically and without unintended recursive calls, maintaining state integrity. - ---- - -## Storage - -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose/modules/NonReentrancyMod.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 internal _lock; - - function protectedAction() external { - // Enter the non-reentrant lock. The lock state is managed internally by the library. - _lock.enter(); - - // Perform sensitive operations here... - // If another external call tries to re-enter this function while it's executing, - // it will revert due to the active lock. - - // Exit the non-reentrant lock, allowing future calls. - _lock.exit(); - } -}`} - - -## Best Practices - - -- Use `_lock.enter()` at the beginning of functions vulnerable to reentrancy and `_lock.exit()` at the end, before any external calls or state changes that could trigger reentrancy. -- Ensure the `_lock` variable is correctly initialized and managed according to the library's expectations. Typically, this involves a storage slot dedicated to the reentrancy lock. -- Handle the `Reentrancy` error explicitly in calling facets to gracefully manage situations where a reentrant call is detected. - - -## Integration Notes - - -The `NonReentrancyMod` operates by managing a lock state, typically stored within a facet. The `enter` function sets the lock, and the `exit` function releases it. The library expects to be used with a `uint256` or equivalent type that can store the lock's state. Facets integrating this module must ensure that the storage slot used for the lock is appropriately managed and that the `enter` and `exit` functions are called in the correct sequence to maintain the reentrancy guard's integrity. - - -
- -
- - diff --git a/website/docs/contracts/libraries/_category_.json b/website/docs/contracts/libraries/_category_.json deleted file mode 100644 index 73e90bb3..00000000 --- a/website/docs/contracts/libraries/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Utilities", - "position": 4, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Utility libraries and helpers for diamond development.", - "slug": "/docs/contracts/libraries" - } -} diff --git a/website/docs/contracts/token/ERC1155/ERC1155Facet.mdx b/website/docs/contracts/token/ERC1155/ERC1155Facet.mdx deleted file mode 100644 index a182a268..00000000 --- a/website/docs/contracts/token/ERC1155/ERC1155Facet.mdx +++ /dev/null @@ -1,664 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "Manages ERC-1155 fungible and non-fungible tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 fungible and non-fungible tokens. - - - -- Supports both fungible and non-fungible tokens. -- Implements batch transfers for efficiency. -- Provides flexible URI resolution for token metadata. - - -## Overview - -The ERC1155Facet provides a comprehensive implementation for the ERC-1155 multi-token standard. It enables the management of fungible and non-fungible tokens within a Compose diamond, supporting batch transfers, approvals, and token URI resolution. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; -import {ERC1155Facet} from "@compose/contracts/src/facets/ERC1155/ERC1155Facet.sol"; - -contract ERC1155User { - IERC1155Facet public erc1155Facet; - - constructor(address _diamondAddress) { - erc1155Facet = IERC1155Facet(_diamondAddress); - } - - function getTokenUri(uint256 _id) public view returns (string memory) { - return erc1155Facet.uri(_id); - } - - function getBalance(address _account, uint256 _id) public view returns (uint256) { - return erc1155Facet.balanceOf(_account, _id); - } -}`} - - -## Best Practices - - -- Initialize the ERC1155 facet with a base URI if token URIs will be concatenated. -- Use `setApprovalForAll` to grant operator permissions for transfers. -- Store and manage token IDs and their associated data carefully to prevent mismatches. - - -## Security Considerations - - -Ensure that `safeTransferFrom` and `safeBatchTransferFrom` correctly validate recipient contract interfaces to prevent reentrancy or unexpected behavior. Access control for minting/burning functions (if implemented in other facets) must be robust. Batch operations require careful length validation for all input arrays to prevent logical errors. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC1155/ERC1155Mod.mdx b/website/docs/contracts/token/ERC1155/ERC1155Mod.mdx deleted file mode 100644 index 82d50b73..00000000 --- a/website/docs/contracts/token/ERC1155/ERC1155Mod.mdx +++ /dev/null @@ -1,605 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "Manage ERC-1155 token transfers, minting, and burning within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC-1155 token transfers, minting, and burning within a diamond. - - - -- Supports minting and burning of single or multiple ERC-1155 token types. -- Implements safe transfer logic for single and batch transfers, including receiver validation. -- Allows setting base and token-specific URIs for metadata. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides a comprehensive implementation for handling ERC-1155 tokens, enabling minting, burning, and safe transfers. It adheres to EIP-1155 standards, ensuring interoperability and secure token management. Integrating this module allows your diamond to function as an ERC-1155 token issuer and manager. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { -mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; -mapping(address account => mapping(address operator => bool)) isApprovedForAll; -string uri; -string baseURI; -mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Mod} from "./interfaces/IERC1155Mod.sol"; - -contract MyDiamondFacet { - IERC1155Mod internal constant ERC1155 = IERC1155Mod(address(this)); - - function _mintTokens(address _to, uint256 _id, uint256 _amount) external { - ERC1155.mint(_to, _id, _amount); - } - - function _burnTokens(address _from, uint256 _id, uint256 _amount) external { - ERC1155.burn(_from, _id, _amount); - } - - function _safeTransfer(address _from, address _to, uint256 _id, uint256 _amount) external { - ERC1155.safeTransferFrom(_from, _to, _id, _amount, ""); - } - - function _setTokenURI(uint256 _tokenId, string memory _uri) external { - ERC1155.setTokenURI(_tokenId, _uri); - } -}`} - - -## Best Practices - - -- Implement access control for minting and burning functions to prevent unauthorized operations. -- Carefully validate receiver addresses in transfer functions to prevent sending tokens to invalid destinations. -- Ensure proper ERC1155Receiver interface implementation for contracts receiving tokens to handle safe transfers correctly. - - -## Integration Notes - - -This module utilizes a dedicated storage slot for its ERC-1155 state, including balances, approvals, and URI mappings. Facets interacting with this module can access its state via the `getStorage` function or by calling the module's functions directly through the diamond proxy. Changes to token balances or URIs are immediately reflected and visible to all facets. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC1155/_category_.json b/website/docs/contracts/token/ERC1155/_category_.json deleted file mode 100644 index 71fe6a13..00000000 --- a/website/docs/contracts/token/ERC1155/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-1155", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-1155 multi-token implementations.", - "slug": "/docs/contracts/token/ERC1155" - } -} diff --git a/website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx deleted file mode 100644 index 1492f69f..00000000 --- a/website/docs/contracts/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ /dev/null @@ -1,246 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "Burn ERC-20 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC-20 tokens within a Compose diamond. - - - -- Enables burning of ERC-20 tokens directly via the diamond proxy. -- Supports burning from the caller's balance and from an approved allowance. -- Emits `Transfer` events to the zero address upon successful burning, adhering to ERC-20 standards. - - -## Overview - -The ERC20BurnFacet allows burning of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account using their allowance, ensuring compliance with ERC-20 standards and emitting necessary events. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; - -contract ERC20BurnConsumer { - IERC20BurnFacet constant ERC20_BURN = IERC20BurnFacet(address(1)); // Replace with actual diamond address - - function consumeBurn(address _tokenAddress, uint256 _amount) external { - // Assuming ERC20BurnFacet is implemented for _tokenAddress - ERC20_BURN.burn(_amount); - } - - function consumeBurnFrom(address _tokenAddress, address _from, uint256 _amount) external { - // Assuming ERC20BurnFacet is implemented for _tokenAddress - ERC20_BURN.burnFrom(_from, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC20BurnFacet is correctly deployed and selectors are added to the diamond proxy. -- Use the `burn` function to remove tokens from the caller's balance. -- Utilize `burnFrom` when an allowance has been granted to the caller for another user's tokens. - - -## Security Considerations - - -The `burn` and `burnFrom` functions require sufficient balance and allowance respectively. The `burnFrom` function relies on the ERC20 `approve` mechanism being correctly implemented and used by the token owner. Ensure adequate access control is applied at the diamond proxy level if certain users should not have access to these functions. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx deleted file mode 100644 index ab448770..00000000 --- a/website/docs/contracts/token/ERC20/ERC20/ERC20Facet.mdx +++ /dev/null @@ -1,569 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "Implements the ERC-20 token standard on Compose diamonds." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements the ERC-20 token standard on Compose diamonds. - - - -- Implements the full ERC-20 standard interface. -- Utilizes the Compose diamond storage pattern for state management. -- Supports token transfers, balance queries, and approval mechanisms. - - -## Overview - -The ERC20Facet provides standard ERC-20 token functionality, enabling tokens to be managed, transferred, and approved within a Compose diamond. It exposes core ERC-20 methods like `transfer`, `balanceOf`, and `approve`, integrating seamlessly with the diamond's storage pattern. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20Facet} from "@compose/contracts/src/facets/ERC20Facet.sol"; -import {DiamondProxy} from "@compose/contracts/src/DiamondProxy.sol"; -import {DiamondInit} from "@compose/contracts/src/DiamondInit.sol"; - -contract MyTokenDiamond is DiamondProxy { - ERC20Facet public erc20Facet; - - function deploy(bytes memory _diamondInit, bytes[] memory _facetCuts) public payable { - // ... deployment logic ... - } - - function setErc20Facet(address _erc20Facet) public onlyOwner { - erc20Facet = ERC20Facet(_erc20Facet); - } - - // Example usage of ERC20Facet functions - function getTokenName() public view returns (string memory) { - return erc20Facet.name(); - } - - function transferTokens(address _to, uint256 _amount) public { - erc20Facet.transfer(_to, _amount); - } -}`} - - -## Best Practices - - -- Initialize the ERC20Facet with the desired token name, symbol, and decimals during diamond deployment. -- Ensure adequate allowance is set before calling `transferFrom` by using the `approve` function. -- Access the facet's storage using the `getStorage` internal function within the facet itself. - - -## Security Considerations - - -This facet adheres to standard ERC-20 security practices. Ensure proper input validation and access control are implemented by any facets that interact with `transfer` or `transferFrom` to prevent vulnerabilities like reentrancy or unauthorized transfers. Use custom errors for clearer revert reasons. The `approve` function requires careful handling to prevent allowance manipulation. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx deleted file mode 100644 index 7f9348a0..00000000 --- a/website/docs/contracts/token/ERC20/ERC20/ERC20Mod.mdx +++ /dev/null @@ -1,422 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "ERC-20 token standard implementation and state management." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token standard implementation and state management. - - - -- Implements standard ERC-20 functions: transfer, approve, transferFrom. -- Supports minting and burning of tokens, updating total supply accordingly. -- Provides a `getStorage` function to access the ERC-20 state within the diamond. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Mod provides the core logic and storage structure for implementing the ERC-20 token standard within a Compose diamond. It enables standard token operations like transfers, approvals, minting, and burning, while adhering to the diamond's storage pattern for composability and upgradeability. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -string symbol; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod } from "@compose/modules/erc20/IERC20Mod.sol"; -import {ERC20Storage } from "@compose/modules/erc20/ERC20Storage.sol"; - -contract MyERC20Facet { - IERC20Mod internal constant ERC20 = IERC20Mod(address(this)); - - function transferToken(address _to, uint256 _amount) external { - address sender = msg.sender; - ERC20.transfer(sender, _to, _amount); - } - - function approveSpender(address _spender, uint256 _amount) external { - address owner = msg.sender; - ERC20.approve(owner, _spender, _amount); - } - - function mintTokens(address _to, uint256 _amount) external { - address minter = msg.sender; - ERC20.mint(minter, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure the ERC20Storage struct is correctly laid out and initialized in the diamond's deployment. -- Use custom errors for all revert conditions to improve gas efficiency and clarity. -- Always check for the zero address when transferring or approving tokens to prevent unintended behavior. - - -## Integration Notes - - -The ERC20Mod utilizes a dedicated storage slot for its `ERC20Storage` struct. Facets interacting with ERC-20 functionality should import `IERC20Mod` and `ERC20Storage` and use the `getStorage` function or directly reference the storage layout if within the same facet. Ensure no other facets overwrite the ERC-20 storage slot. The `mint` and `burn` functions directly manipulate the total supply and individual balances, impacting all ERC-20 operations. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20/_category_.json b/website/docs/contracts/token/ERC20/ERC20/_category_.json deleted file mode 100644 index 9e5771df..00000000 --- a/website/docs/contracts/token/ERC20/ERC20/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 fungible token implementations.", - "slug": "/docs/contracts/token/ERC20/ERC20" - } -} diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx deleted file mode 100644 index 67b922d8..00000000 --- a/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,443 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain token transfers for ERC-20 tokens." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Facilitates cross-chain token transfers for ERC-20 tokens. - - - -- Authorizes cross-chain minting and burning exclusively to addresses with the `trusted-bridge` role. -- Provides internal checks (`checkTokenBridge`) to validate bridge permissions before executing cross-chain operations. -- Exposes functions to retrieve internal ERC-20 and Access Control storage structures. - - -## Overview - -The ERC20BridgeableFacet enables secure and authorized cross-chain minting and burning of ERC-20 tokens within a Compose diamond. It ensures that only trusted bridge addresses can initiate these operations, maintaining the integrity of token supply across different chains. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### getAccessControlStorage - - -{`function getAccessControlStorage() internal pure returns (AccessControlStorage storage s);`} - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableFacet} from "@compose-protocol/diamond/contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; -import {DiamondInterface} from "@compose-protocol/diamond/contracts/DiamondInterface.sol"; - -contract ERC20BridgeableConsumer { - address internal diamondAddress; - - // Define selectors for the facet functions - bytes4 internal constant CROSSCHAIN_MINT_SELECTOR = - IERC20BridgeableFacet.crosschainMint.selector; - bytes4 internal constant CROSSCHAIN_BURN_SELECTOR = - IERC20BridgeableFacet.crosschainBurn.selector; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - /** - * @notice Mints ERC20 tokens on a remote chain via the diamond proxy. - * @param _token The address of the ERC20 token. - * @param _to The recipient address on the remote chain. - * @param _amount The amount of tokens to mint. - */ - function mintRemote(address _token, address _to, uint256 _amount) external { - (bool success, ) = diamondAddress.call(abi.encodeWithSelector( - CROSSCHAIN_MINT_SELECTOR, - _token, - _to, - _amount - )); - require(success, "ERC20BridgeableConsumer: crosschain mint failed"); - } - - /** - * @notice Burns ERC20 tokens on a remote chain via the diamond proxy. - * @param _token The address of the ERC20 token. - * @param _from The sender address on the remote chain. - * @param _amount The amount of tokens to burn. - */ - function burnRemote(address _token, address _from, uint256 _amount) external { - (bool success, ) = diamondAddress.call(abi.encodeWithSelector( - CROSSCHAIN_BURN_SELECTOR, - _token, - _from, - _amount - )); - require(success, "ERC20BridgeableConsumer: crosschain burn failed"); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is correctly assigned to authorized cross-chain bridge addresses in the AccessControl facet. -- Verify that the ERC-20 token contract address passed to `crosschainMint` and `crosschainBurn` is valid and accessible. -- Access the facet's storage directly using inline assembly for `getERC20Storage` and `getAccessControlStorage` when querying internal state. - - -## Security Considerations - - -The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role, preventing unauthorized token supply manipulation. The `checkTokenBridge` internal function ensures that only authorized bridge callers can execute these sensitive operations. Reentrancy is not a concern as these functions do not make external calls to untrusted contracts. Ensure the caller address is not zero before calling `checkTokenBridge` to prevent potential `AccessControlUnauthorizedAccount` errors. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx deleted file mode 100644 index a8342e0d..00000000 --- a/website/docs/contracts/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,420 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "Manages cross-chain ERC20 token bridging operations." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages cross-chain ERC20 token bridging operations. - - - -- Enables cross-chain token minting and burning operations. -- Enforces access control via the `trusted-bridge` role for all cross-chain transfers. -- Provides helper functions to access module-specific storage. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module enables secure cross-chain ERC20 token transfers by allowing trusted bridges to mint and burn tokens. It enforces access control to ensure only authorized entities can perform these critical operations, maintaining the integrity of token balances across different chains. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { -mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -}`} - - -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC20BridgeableMod} from "@compose-protocol/diamond-contracts/modules/ERC20BridgeableMod.sol"; -import {DiamondStorage} from "@compose-protocol/diamond-contracts/core/DiamondStorage.sol"; - -contract MyFacet { - using ERC20BridgeableMod for ERC20BridgeableMod.ERC20BridgeableStorage; - using DiamondStorage for DiamondStorage.Storage; - - function exampleCrosschainBurn(address _token, address _to, uint256 _amount) external { - DiamondStorage.Storage storage ds = DiamondStorage.Storage.wrap(address(this)); - ERC20BridgeableMod.ERC20BridgeableStorage storage ebs = ds.erc20BridgeableStorage(); - - // Ensure the caller has the 'trusted-bridge' role before calling - // require(ds.hasRole("trusted-bridge", msg.sender), "Not a trusted bridge"); - - ebs.crosschainBurn(_token, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure that only addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint` functions, typically managed by an AccessControl facet. -- Validate all input parameters for `crosschainBurn` and `crosschainMint` to prevent unexpected behavior or exploits. -- Implement robust logging and event emission for `CrosschainBurn` and `CrosschainMint` to facilitate monitoring and auditing of cross-chain operations. - - -## Integration Notes - - -The `ERC20BridgeableMod` utilizes a dedicated storage slot for its state, accessible via `getERC20Storage`. Functions like `crosschainBurn` and `crosschainMint` operate on this storage. Access control checks for the `trusted-bridge` role are performed internally, relying on the presence and correct configuration of an AccessControl facet. Ensure that the `ERC20BridgeableMod` storage struct is correctly initialized and that the `trusted-bridge` role is properly managed by an AccessControl facet. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json deleted file mode 100644 index c894aef5..00000000 --- a/website/docs/contracts/token/ERC20/ERC20Bridgeable/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20 Bridgeable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 Bridgeable extension for ERC-20 tokens.", - "slug": "/docs/contracts/token/ERC20/ERC20Bridgeable" - } -} diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx deleted file mode 100644 index 97aabf97..00000000 --- a/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ /dev/null @@ -1,339 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "Manages ERC-20 token permits via EIP-2612 signatures." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-20 token permits via EIP-2612 signatures. - - - -- Implements EIP-2612 standard for ERC-20 permits. -- Utilizes domain separators to prevent cross-chain replay attacks. -- Manages nonces to ensure each permit is used only once. - - -## Overview - -The ERC20PermitFacet enables users to grant allowances to token spenders without the spender needing to initiate a transaction. It implements the EIP-2612 standard, allowing for off-chain signing of permit messages, which are then submitted on-chain to set allowances. This facet enhances composability by reducing the need for token holders to interact directly with the token contract for every allowance grant. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - -### State Variables - - - -## Functions - -### getERC20Storage - - -{`function getERC20Storage() internal pure returns (ERC20Storage storage s);`} - - ---- -### getStorage - - -{`function getStorage() internal pure returns (ERC20PermitStorage storage s);`} - - ---- -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Permit, ERC20PermitFacet} from "@compose-protocol/diamond/contracts/facets/ERC20Permit/ERC20PermitFacet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond/contracts/Diamond.sol"; - -contract Deployer { - address diamondAddress; - - function deployDiamond() external { - // ... deployment logic for diamond and facets ... - // Assume ERC20PermitFacet is deployed and its address is known - address erc20PermitFacetAddress = address(new ERC20PermitFacet()); - - // Add the ERC20PermitFacet to the diamond - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); - cut[0] = IDiamondCut.FacetCut({ - facetAddress: erc20PermitFacetAddress, - action: IDiamondCut.FacetCutAction.Add, - selectors: - ERC20PermitFacet.getSelectors() // Assuming a helper function to get selectors - }); - // diamond.diamondCut(cut, address(0), ""); // Call diamondCut on your diamond instance - } - - function permitERC20(address tokenAddress, address spender, uint256 value, uint256 deadline, bytes calldata signature) external { - // Assuming the diamond has been cut with ERC20PermitFacet - // Call the permit function through the diamond proxy - // The diamond will route this call to the ERC20PermitFacet - // The actual function call on the diamond will be: - // ERC20PermitFacet(diamondAddress).permit(tokenAddress, spender, value, deadline, signature); - } -}`} - - -## Best Practices - - -- Initialize the `ERC20PermitFacet` with the correct ERC-20 token address during deployment or upgrade. -- Ensure the `deadline` parameter in the `permit` function is set appropriately to prevent stale signatures. -- Verify the signature's validity and the owner's intent before submitting the `permit` transaction. - - -## Security Considerations - - -The `permit` function relies on off-chain signatures. Ensure robust off-chain signing mechanisms are in place to prevent signature malleability or replay attacks. The `deadline` parameter is critical; if set too far in the future, it could allow a compromised key to grant allowances indefinitely until the deadline passes. Input validation for `spender`, `value`, and `deadline` should be handled carefully. The facet should be initialized with the correct ERC-20 token address to prevent granting permits for unintended tokens. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx deleted file mode 100644 index 616f9fd5..00000000 --- a/website/docs/contracts/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ /dev/null @@ -1,291 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "ERC-2612 Permit functionality and domain separator" -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2612 Permit functionality and domain separator - - - -- Implements ERC-2612 permit functionality for gasless approvals. -- Manages and provides the domain separator for signature generation. -- Validates permit signatures to ensure authenticity and prevent replay attacks. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides ERC-2612 permit functionality, enabling gasless approvals for ERC-20 tokens. It manages the domain separator and permit validation, allowing users to grant allowances via signed messages without direct on-chain transactions for the approval itself. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { -mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { -mapping(address owner => uint256 balance) balanceOf; -uint256 totalSupply; -mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -uint8 decimals; -string name; -}`} - - -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20PermitMod} from "@compose-protocol/diamond-contracts/contracts/modules/ERC20Permit/IERC20PermitMod.sol"; - -contract MyERC20Facet { - IERC20PermitMod internal constant _erc20Permit = IERC20PermitMod(address(0)); // Replace with actual diamond address - - /** - * @notice Allows a user to permit a spender for an allowance via a signed permit. - * @dev This function assumes the caller is a facet that will emit the Approval event. - * @param owner The owner of the tokens. - * @param spender The address permitted to spend tokens. - * @param value The amount of tokens that may be spent. - * @param deadline The deadline for the permit. - * @param v The v component of the signature. - * @param r The r component of the signature. - * @param s The s component of the signature. - */ - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // The permit function in the module validates the signature and updates allowances. - // The calling facet is responsible for emitting the Approval event as per ERC-20 and ERC-2612 standards. - _erc20Permit.permit(owner, spender, value, deadline, v, r, s); - // Emit Approval event here after successful permit validation - // emit Approval(owner, spender, value); // This event emission should be handled by the facet that calls permit - } - - /** - * @notice Retrieves the domain separator for ERC-20 permit signatures. - * @return The domain separator. - */ - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _erc20Permit.DOMAIN_SEPARATOR(); - } -} -`} - - -## Best Practices - - -- Ensure the `Approval` event is emitted by the calling facet after a successful `permit` call to comply with ERC-20/ERC-2612 standards. -- Validate the `deadline` parameter to prevent the use of expired permits. - - -## Integration Notes - - -This module interacts with the diamond's storage to manage ERC-20 allowances and permit data. Facets calling the `permit` function are responsible for emitting the `Approval` event. The `DOMAIN_SEPARATOR` is chain and contract specific, ensuring signature validity only within the context of the deployed diamond. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json b/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json deleted file mode 100644 index 05d8340d..00000000 --- a/website/docs/contracts/token/ERC20/ERC20Permit/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20 Permit", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 Permit extension for ERC-20 tokens.", - "slug": "/docs/contracts/token/ERC20/ERC20Permit" - } -} diff --git a/website/docs/contracts/token/ERC20/_category_.json b/website/docs/contracts/token/ERC20/_category_.json deleted file mode 100644 index 561dc476..00000000 --- a/website/docs/contracts/token/ERC20/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-20 fungible token implementations.", - "slug": "/docs/contracts/token/ERC20" - } -} diff --git a/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx deleted file mode 100644 index 55d97f54..00000000 --- a/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ /dev/null @@ -1,538 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "Manages ERC-6909 token balances and operator approvals." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-6909 token balances and operator approvals. - - - -- Implements the ERC-6909 token standard for granular asset management. -- Supports standard token transfer and approval functions. -- Includes operator functionality for delegated management of assets. - - -## Overview - -The ERC6909Facet implements the ERC-6909 standard, enabling granular control over token balances and operator permissions within a Compose diamond. It provides essential functions for querying balances, allowances, and operator status, as well as performing token transfers and managing operator relationships. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909} from "@compose/contracts/src/interfaces/IERC6909.sol"; -import {ERC6909Facet} from "@compose/contracts/src/facets/ERC6909Facet.sol"; - -contract MyDiamond is IERC6909 { - // ... other facet interfaces and implementations - - function transfer(address to, uint256 amount) external override returns (bool) { - return ERC6909Facet(this).transfer(to, amount); - } - - function transferFrom(address from, address to, uint256 amount) external override returns (bool) { - return ERC6909Facet(this).transferFrom(from, to, amount); - } - - function approve(address spender, uint256 amount) external override { - ERC6909Facet(this).approve(spender, amount); - } - - function balanceOf(address owner) external view override returns (uint256) { - return ERC6909Facet(this).balanceOf(owner); - } - - function allowance(address owner, address spender) external view override returns (uint256) { - return ERC6909Facet(this).allowance(owner, spender); - } - - function setOperator(address operator, bool approved) external override { - ERC6909Facet(this).setOperator(operator, approved); - } - - function isOperator(address owner, address operator) external view override returns (bool) { - return ERC6909Facet(this).isOperator(owner, operator); - } -}`} - - -## Best Practices - - -- Initialize the ERC6909Facet with appropriate access controls if necessary, though many functions are permissionless. -- When transferring tokens, ensure sufficient balance and allowance checks are performed by the caller or handled by the facet's error reporting. -- Use `setOperator` judiciously to grant or revoke permissions, understanding the implications for operator-driven transactions. - - -## Security Considerations - - -The facet relies on the diamond proxy for access control. Ensure that sensitive operations are appropriately guarded by the diamond's access control mechanism. Standard reentrancy guards are recommended for functions that modify state and can be called externally. Input validation is handled by custom errors to prevent invalid operations. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx deleted file mode 100644 index 7f45c057..00000000 --- a/website/docs/contracts/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ /dev/null @@ -1,517 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "Manages ERC-6909 token balances, approvals, and operators." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-6909 token balances, approvals, and operators. - - - -- Manages balances, approvals, and operator roles for multiple token IDs. -- Supports infinite approvals (`type(uint256).max`). -- Operator transfers are bypass allowance checks. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides essential internal functions and storage layout for implementing ERC-6909 minimal multi-token functionality within a Compose diamond. It enables efficient management of token balances, approvals, and operator roles, crucial for composable token standards. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { -mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; -mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; -mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Mod} from "./IERC6909Mod.sol"; -import {ERC6909Mod} from "./ERC6909Mod.sol"; - -contract MyTokenFacet { - // Assume STORAGE_POSITION is defined elsewhere and points to the ERC6909Mod storage slot - using ERC6909Mod for ERC6909Mod.ERC6909Storage; - - function _transferTokens(address _from, address _to, uint256 _id, uint256 _amount) internal { - ERC6909Mod.ERC6909Storage storage erc6909Storage = ERC6909Mod.getStorage(); - erc6909Storage.transfer(_from, _to, _id, _amount); - } - - function _approveToken(address _spender, uint256 _id, uint256 _amount) internal { - ERC6909Mod.ERC6909Storage storage erc6909Storage = ERC6909Mod.getStorage(); - erc6909Storage.approve(_spender, _id, _amount); - } -}`} - - -## Best Practices - - -- Ensure the `STORAGE_POSITION` for `ERC6909Storage` is correctly defined and immutable. -- Implement access control for functions like `setOperator` if operator registration requires authorization beyond the caller. -- Handle potential `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` errors appropriately in calling facets. - - -## Integration Notes - - -The `ERC6909Mod` requires a dedicated storage slot, defined by `STORAGE_POSITION`, to hold its `ERC6909Storage` struct. Facets using this module must access this storage via the `getStorage()` function. Any facet can read or write to this storage, adhering to the diamond storage pattern. Ensure this storage slot is initialized correctly during deployment and not modified by other facets to maintain data integrity. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC6909/ERC6909/_category_.json b/website/docs/contracts/token/ERC6909/ERC6909/_category_.json deleted file mode 100644 index 24639b1e..00000000 --- a/website/docs/contracts/token/ERC6909/ERC6909/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-6909 minimal multi-token implementations.", - "slug": "/docs/contracts/token/ERC6909/ERC6909" - } -} diff --git a/website/docs/contracts/token/ERC6909/_category_.json b/website/docs/contracts/token/ERC6909/_category_.json deleted file mode 100644 index 82d63b49..00000000 --- a/website/docs/contracts/token/ERC6909/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-6909 minimal multi-token implementations.", - "slug": "/docs/contracts/token/ERC6909" - } -} diff --git a/website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx deleted file mode 100644 index 91b45d59..00000000 --- a/website/docs/contracts/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "Burn ERC721 tokens within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC721 tokens within a diamond. - - - -- Destroys ERC721 tokens, adhering to ERC721 standards. -- Emits `Transfer` event with `to` address as address(0) upon successful burn. -- Requires explicit approval or ownership for burning. -- Integrates seamlessly with the Compose Diamond Proxy pattern. - - -## Overview - -The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens managed by a Compose diamond. It ensures that tokens are removed from enumeration tracking and that associated events are emitted, adhering to ERC721 standards. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "@compose/core/facets/erc721/IERC721BurnFacet.sol"; - -contract BurnCaller { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 tokenId) external { - bytes4 selector = IERC721BurnFacet.burn.selector; - (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, tokenId)); - require(success, "Burn failed"); - } -}`} - - -## Best Practices - - -- Ensure the ERC721BurnFacet is properly initialized with the correct storage slot. -- Use the facet's `burn` function to destroy tokens, as it handles necessary state updates and event emissions. -- Verify that the caller has the necessary approvals (via `ERC721.approve` or `ERC721.setApprovalForAll`) before attempting to burn a token they do not own. - - -## Security Considerations - - -The `burn` function requires that the caller is either the owner of the token or has been approved by the owner to burn it. The facet checks for ERC721InsufficientApproval and ERC721NonexistentToken errors. Ensure that approvals are managed correctly to prevent unauthorized burning. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx deleted file mode 100644 index 80c730f1..00000000 --- a/website/docs/contracts/token/ERC721/ERC721/ERC721Facet.mdx +++ /dev/null @@ -1,662 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "Manages ERC-721 token ownership, transfers, and approvals." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-721 token ownership, transfers, and approvals. - - - -- Full ERC-721 (EIP-721) compliance. -- Supports standard token metadata retrieval (`name`, `symbol`, `tokenURI`). -- Enables ownership tracking and transfer logic. -- Manages token approvals for single tokens and for all tokens. - - -## Overview - -The ERC721Facet provides a standard interface for managing non-fungible tokens within a Compose diamond. It handles core ERC-721 functionalities including token ownership, metadata retrieval, and approval mechanisms, enabling seamless integration with other diamond facets. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-721 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer a token, checking for ownership and approval. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose/diamond/facets/ERC721/IERC721Facet.sol"; - -contract ERC721Consumer { - IERC721Facet private immutable _erc721Facet; - - constructor(address _erc721FacetAddress) { - _erc721Facet = IERC721Facet(_erc721FacetAddress); - } - - function getTokenName() external view returns (string memory) { - return _erc721Facet.name(); - } - - function getOwner(uint256 tokenId) external view returns (address) { - return _erc721Facet.ownerOf(tokenId); - } - - function safeTransfer(address to, uint256 tokenId) external { - // Ensure caller has approval or is the owner - _erc721Facet.safeTransferFrom(msg.sender, to, tokenId); - } -}`} - - -## Best Practices - - -- Initialize the facet with necessary parameters during diamond deployment. -- Ensure proper access control is implemented for functions like `approve` and `setApprovalForAll` if custom logic is required. -- Be mindful of storage slot conflicts if adding custom state to the ERC721 storage struct. - - -## Security Considerations - - -The `internalTransferFrom` function includes essential checks for ownership and approvals. Users must carefully manage approvals to prevent unauthorized transfers. The `safeTransferFrom` functions include receiver validation to mitigate reentrancy risks associated with non-compliant token receivers. Ensure the diamond proxy's access control layer properly restricts sensitive operations if needed. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx deleted file mode 100644 index 2e4b1f81..00000000 --- a/website/docs/contracts/token/ERC721/ERC721/ERC721Mod.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "Internal logic for ERC-721 token management." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Internal logic for ERC-721 token management. - - - -- Comprehensive ERC-721 state management functions (mint, burn, transfer). -- Utilizes diamond storage pattern for persistent and composable state. -- Includes explicit error handling for common ERC-721 operations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721Mod module provides essential internal logic for managing ERC-721 tokens within a Compose diamond. It encapsulates core functionalities like minting, burning, and transferring tokens, ensuring consistent and secure state management through the diamond's storage pattern. This allows custom facets to easily integrate robust ERC-721 capabilities without duplicating complex logic. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256 balance) balanceOf; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; - -contract MyERC721Facet { - IERC721Mod public immutable erc721Mod; - - constructor(address _diamondProxy) { - erc721Mod = IERC721Mod(_diamondProxy); - } - - function mintToken(address _to, uint256 _tokenId) external { - erc721Mod.mint(_to, _tokenId); - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - erc721Mod.transferFrom(_from, _to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - erc721Mod.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC721Mod` facet is initialized and accessible via the diamond proxy. -- Handle specific `ERC721Mod` errors (`ERC721IncorrectOwner`, `ERC721InvalidReceiver`, etc.) in facet logic for robust user feedback. -- Be mindful of token ID uniqueness and the zero address checks enforced by `mint` and `transferFrom`. - - -## Integration Notes - - -The ERC721Mod uses a predefined storage slot to manage its internal ERC721 storage struct. Facets interacting with this module should be aware that all state changes (token ownership, approvals, metadata) are managed internally by ERC721Mod and are reflected across the diamond. The `getStorage` function provides direct access to this internal state, which can be useful for read operations or debugging, but direct modification is not recommended. Ensure the ERC721Mod facet is added to the diamond before any facets that rely on its functionality. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC721/ERC721/_category_.json b/website/docs/contracts/token/ERC721/ERC721/_category_.json deleted file mode 100644 index 267dcc9e..00000000 --- a/website/docs/contracts/token/ERC721/ERC721/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-721 non-fungible token implementations.", - "slug": "/docs/contracts/token/ERC721/ERC721" - } -} diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index e95f7bd1..00000000 --- a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,225 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "Enables burning of ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enables burning of ERC721 tokens within a Compose diamond. - - - -- Supports the burning of ERC721 tokens. -- Integrates with ERC721Enumerable storage to maintain accurate token counts and ownership lists. -- Emits a `Transfer` event with `from` set to the token owner and `to` set to the zero address upon successful burning. - - -## Overview - -The ERC721EnumerableBurnFacet provides the functionality to burn ERC721 tokens. This facet integrates with the ERC721Enumerable storage pattern, ensuring that burned tokens are correctly removed from the enumeration tracking mechanisms, maintaining the integrity of token ownership and supply. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableBurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; -import {ERC721EnumerableBurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/ERC721EnumerableBurnFacet.sol"; - -contract MyDiamond is IERC721EnumerableBurnFacet { - address constant ERC721_ENUMERABLE_BURN_FACET = address(0x...'); // Deployed facet address - - // Other diamond facets and logic - - function burn(uint256 _tokenId) external { - IERC721EnumerableBurnFacet(ERC721_ENUMERABLE_BURN_FACET).burn(_tokenId); - } - - // ... other diamond functions -} - -// To burn a token: -// MyDiamond(diamondAddress).burn(123);`} - - -## Best Practices - - -- Ensure the ERC721EnumerableBurnFacet is correctly initialized with the diamond's ownership and approval logic. -- Verify that the `burn` function is called with a valid token ID that exists and is owned by the caller or approved for transfer. -- When upgrading, ensure the new facet implementation maintains compatibility with existing storage layouts. - - -## Security Considerations - - -The `burn` function requires proper access control to ensure only the token owner or an approved address can burn a token. The facet must correctly handle cases where a token ID does not exist, reverting with `ERC721NonexistentToken`. Ensure sufficient approval checks are in place before burning, reverting with `ERC721InsufficientApproval` if necessary. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx deleted file mode 100644 index d797fe52..00000000 --- a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,742 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "Enumerable ERC-721 token management and metadata." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enumerable ERC-721 token management and metadata. - - - -- Full ERC-721 compliance with enumeration capabilities. -- Supports standard NFT metadata retrieval via `tokenURI`. -- Includes internal transfer logic for integration with other facets. -- Emits standard ERC-721 events for `Transfer`, `Approval`, and `ApprovalForAll`. - - -## Overview - -The ERC721EnumerableFacet provides standard ERC-721 functionality including token enumeration, ownership tracking, and metadata access. It integrates seamlessly into a Compose diamond, offering a comprehensive surface area for NFT operations. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage struct used by this facet. - - -{`function getStorage() internal pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### internalTransferFrom - -Internal function to transfer ownership of a token ID. - - -{`function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableFacet} from "@compose-protocol/diamond/facets/ERC721/IERC721EnumerableFacet.sol"; - -contract MyDiamondUser { - IERC721EnumerableFacet immutable erc721Facet; - - constructor(address diamondAddress) { - // Assume diamondAddress is the address of your Compose diamond - // Selector for name() is 0x06395705 - bytes4 nameSelector = 0x06395705; - erc721Facet = IERC721EnumerableFacet(diamondAddress); - } - - function getTokenName() external view returns (string memory) { - return erc721Facet.name(); - } - - function getTotalSupply() external view returns (uint256) { - return erc721Facet.totalSupply(); - } - - function getTokenOwner(uint256 tokenId) external view returns (address) { - return erc721Facet.ownerOf(tokenId); - } - - function getTokenURI(uint256 tokenId) external view returns (string memory) { - return erc721Facet.tokenURI(tokenId); - } -}`} - - -## Best Practices - - -- Initialize the `ERC721EnumerableFacet` with the correct owner and token details during diamond deployment. -- When transferring tokens, prefer `safeTransferFrom` for enhanced security, especially when interacting with unknown receiver contracts. -- Ensure proper access control is implemented at the diamond proxy level if certain functions require specific permissions. - - -## Security Considerations - - -This facet implements standard ERC-721 logic. Ensure that the diamond proxy enforces appropriate access controls for functions like `approve` and `setApprovalForAll`. The `safeTransferFrom` functions include checks for receiver contract compatibility, mitigating reentrancy risks when transferring to contracts. Validate inputs to prevent errors like `ERC721OutOfBoundsIndex` or `ERC721NonexistentToken`. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx deleted file mode 100644 index da702238..00000000 --- a/website/docs/contracts/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,348 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "Manages enumerable ERC721 tokens within a diamond." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages enumerable ERC721 tokens within a diamond. - - - -- Manages the internal state for enumerable ERC721 tokens. -- Provides `mint`, `burn`, and `transferFrom` functions that update enumeration lists. -- Utilizes inline assembly via `getStorage` to access its state efficiently from the diamond. -- Emits standard ERC721 `Transfer` events when token states change. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721EnumerableMod provides essential internal logic for managing enumerable ERC721 tokens. It ensures tokens are correctly added to and removed from internal tracking lists during minting, burning, and transfers, maintaining a consistent and auditable token supply. This module facilitates the integration of standard ERC721 enumerable behavior into custom diamond facets. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { -mapping(uint256 tokenId => address owner) ownerOf; -mapping(address owner => uint256[] ownerTokens) ownerTokens; -mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; -uint256[] allTokens; -mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; -mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; -mapping(uint256 tokenId => address approved) approved; -string name; -string symbol; -string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod} from "@compose/modules/erc721/ERC721EnumerableMod.sol"; - -contract MyERC721Facet { - IERC721EnumerableMod internal enumerableMod; - - constructor(address _enumerableModAddress) { - enumerableMod = IERC721EnumerableMod(_enumerableModAddress); - } - - function mintToken(address _to, uint256 _tokenId) external { - // ... other ERC721 logic ... - enumerableMod.mint(_to, _tokenId); - // ... emit Transfer event ... - } - - function burnToken(uint256 _tokenId) external { - // ... get owner and check approvals ... - enumerableMod.burn(_tokenId); - // ... emit Transfer event ... - } - - function transferToken(address _from, address _to, uint256 _tokenId) external { - // ... get owner and check approvals ... - enumerableMod.transferFrom(_from, _to, _tokenId); - // ... emit Transfer event ... - } -}`} - - -## Best Practices - - -- Ensure the `ERC721EnumerableMod` contract is correctly initialized with its facet address. -- Always check for and handle the custom errors emitted by the module (e.g., `ERC721NonexistentToken`, `ERC721InvalidReceiver`). -- Integrate module calls within a transaction to maintain atomicity of token state and enumeration updates. - - -## Integration Notes - - -This module interacts with the diamond's storage using a predefined slot for its internal ERC721 enumerable storage struct. Facets integrating this module will call its functions to perform token operations. The module's internal state changes are directly reflected in the diamond's storage, making them visible to all facets that access the relevant storage slots. The order of operations within a facet is critical to ensure the module's state correctly reflects the token's lifecycle. - - -
- -
- - diff --git a/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json deleted file mode 100644 index 8eb69a47..00000000 --- a/website/docs/contracts/token/ERC721/ERC721Enumerable/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-721 Enumerable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-721 Enumerable extension for ERC-721 tokens.", - "slug": "/docs/contracts/token/ERC721/ERC721Enumerable" - } -} diff --git a/website/docs/contracts/token/ERC721/_category_.json b/website/docs/contracts/token/ERC721/_category_.json deleted file mode 100644 index 8dc152ec..00000000 --- a/website/docs/contracts/token/ERC721/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-721 non-fungible token implementations.", - "slug": "/docs/contracts/token/ERC721" - } -} diff --git a/website/docs/contracts/token/Royalty/RoyaltyFacet.mdx b/website/docs/contracts/token/Royalty/RoyaltyFacet.mdx deleted file mode 100644 index 004c53f7..00000000 --- a/website/docs/contracts/token/Royalty/RoyaltyFacet.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "Handles ERC-2981 royalty information." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Handles ERC-2981 royalty information. - - - -- Implements ERC-2981 `royaltyInfo` standard. -- Supports token-specific and default royalty configurations. -- Calculates royalty amounts based on sale price and basis points. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard, enabling dApps to query royalty information for NFTs. It provides a standardized way to retrieve royalty details for specific tokens, supporting both token-specific and default royalty configurations. This facet integrates seamlessly into Compose diamonds, enhancing NFT marketplaces and secondary sales. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the royalty storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() internal pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyFacet} from "@compose-protocol/diamond/contracts/facets/Royalty/IRoyaltyFacet.sol"; - -diamond interface IDiamond { - function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount); -} - -contract MarketplaceExample { - IDiamond immutable diamond; - - constructor(address _diamondAddress) { - diamond = IDiamond(_diamondAddress); - } - - function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { - // Call the royaltyInfo function through the diamond proxy - (receiver, royaltyAmount) = diamond.royaltyInfo(_tokenId, _salePrice); - return (receiver, royaltyAmount); - } -}`} - - -## Best Practices - - -- Ensure the RoyaltyFacet is correctly initialized with default royalty configurations during diamond deployment. -- Access royalty information via the diamond proxy to leverage its routing and access control mechanisms. -- Store royalty-related configurations efficiently, ideally using the provided storage slot to avoid conflicts. - - -## Security Considerations - - -The `royaltyInfo` function is `view` and does not modify state, mitigating reentrancy risks. Ensure that the default royalty receiver and basis points are set appropriately during initialization to prevent unintended royalty distributions. Access to modify royalty settings should be strictly controlled by the diamond's access control layer. - - -
- -
- - diff --git a/website/docs/contracts/token/Royalty/RoyaltyMod.mdx b/website/docs/contracts/token/Royalty/RoyaltyMod.mdx deleted file mode 100644 index 01215014..00000000 --- a/website/docs/contracts/token/Royalty/RoyaltyMod.mdx +++ /dev/null @@ -1,356 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "Manages ERC-2981 royalties with default and token-specific settings." -gitSource: "https://github.com/maxnorm/Compose/blob/00106e341b257629f718c654a349c0ad60a7cf8a/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-2981 royalties with default and token-specific settings. - - - -- Supports both default and token-specific royalty configurations. -- Implements ERC-2981 standard for royalty payments. -- Provides functions to set, delete, and query royalty information. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides a robust implementation for ERC-2981 royalty standards, enabling diamonds to manage both default and token-specific royalty configurations. It ensures that royalty information is accurately queried, supporting a composable approach to NFT sales and revenue distribution. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { -address receiver; -uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { -RoyaltyInfo defaultRoyaltyInfo; -mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "@compose/modules/royalty/IRoyaltyMod.sol"; -import {RoyaltyMod} from "@compose/modules/royalty/RoyaltyMod.sol"; // Example import path - -contract MyNFtFacet { - // Assume IRoyaltyMod is correctly registered with the diamond proxy - IRoyaltyMod internal royaltyMod; - - function initialize(address _diamondAddress) external { - // In a real scenario, this would be set via diamond proxy initialization - royaltyMod = IRoyaltyMod(_diamondAddress); - } - - /** - * @notice Sets royalty for a specific token. - * @param _tokenId The ID of the token. - * @param _receiver The address to receive royalties. - * @param _feeBasisPoints The royalty fee in basis points. - */ - function setTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeBasisPoints) external { - royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeBasisPoints); - } - - /** - * @notice Gets royalty information for a token and sale price. - * @param _tokenId The ID of the token. - * @param _salePrice The sale price of the token. - * @return receiver The address receiving royalties. - * @return feeAmount The calculated royalty amount. - */ - function getTokenRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 feeAmount) { - (receiver, feeAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); - return (receiver, feeAmount); - } -}`} - - -## Best Practices - - -- Always validate receiver addresses and fee basis points when setting royalties to prevent errors and exploits. -- Utilize `resetTokenRoyalty` to efficiently revert to default royalty settings for a token, simplifying management. -- Be aware that `deleteDefaultRoyalty` will cause `royaltyInfo` to return `(address(0), 0)` for tokens without specific royalty configurations. - - -## Integration Notes - - -The RoyaltyMod stores its state in a dedicated storage slot within the diamond proxy. Facets interact with this module via its interface. Changes to default or token-specific royalties are immediately reflected when calling `royaltyInfo`. The `getStorage` function provides direct access to the module's storage struct, useful for off-chain indexing or advanced logic, but direct manipulation is discouraged in favor of the provided functions. - - -
- -
- - diff --git a/website/docs/contracts/token/Royalty/_category_.json b/website/docs/contracts/token/Royalty/_category_.json deleted file mode 100644 index 10ec448e..00000000 --- a/website/docs/contracts/token/Royalty/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Royalty", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "generated-index", - "description": "ERC-2981 royalty standard implementations.", - "slug": "/docs/contracts/token/Royalty" - } -} diff --git a/website/docs/contracts/token/_category_.json b/website/docs/contracts/token/_category_.json deleted file mode 100644 index f10e9138..00000000 --- a/website/docs/contracts/token/_category_.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "label": "Token Standards", - "position": 3, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "Token standard implementations for Compose diamonds.", - "slug": "/docs/contracts/token" - } -} diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json deleted file mode 100644 index 04125e1e..00000000 --- a/website/docs/library/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Library", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/index" - } -} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx deleted file mode 100644 index 06c9edde..00000000 --- a/website/docs/library/access/AccessControl/AccessControlFacet.mdx +++ /dev/null @@ -1,520 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlFacet" -description: "Manages role-based access control within a Compose diamond." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages role-based access control within a Compose diamond. - - - -- Hierarchical role administration: Roles can have their own designated admin roles. -- Batch operations for granting and revoking roles efficiently. -- Explicit error types for unauthorized access attempts. - - -## Overview - -This facet provides a robust role-based access control (RBAC) system, enabling granular permission management for any Compose diamond. It allows defining roles, assigning them to accounts, and enforcing role requirements on function calls, ensuring that only authorized entities can perform sensitive operations. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### hasRole - -Returns if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - ---- -### getRoleAdmin - -Returns the admin role for a role. - - -{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} - - -**Parameters:** - - - ---- -### grantRole - -Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### revokeRole - -Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### grantRoleBatch - -Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### revokeRoleBatch - -Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} - - -**Parameters:** - - - ---- -### renounceRole - -Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. - - -{`function renounceRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when the sender is not the account to renounce the role from. -
- -
- Signature: - -error AccessControlUnauthorizedSender(address _sender, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {AccessControlFacet} from "@compose/access-control/AccessControlFacet.sol"; -import {DiamondProxy} from "@compose/diamond/DiamondProxy.sol"; - -contract MyDiamond is DiamondProxy { - constructor(address _diamondAdmin, address[] memory _initFacets) DiamondProxy(_diamondAdmin, _initFacets) {} - - function grantAdminRole() external { - // Example: Granting the DEFAULT_ADMIN_ROLE to the contract deployer - address deployer = msg.sender; - AccessControlFacet acFacet = AccessControlFacet(address(this)); - bytes32 adminRole = acFacet.getRoleAdmin(AccessControlFacet.DEFAULT_ADMIN_ROLE); - acFacet.grantRole(adminRole, deployer); - } - - function hasAdminRole(address _account) external view returns (bool) { - AccessControlFacet acFacet = AccessControlFacet(address(this)); - return acFacet.hasRole(AccessControlFacet.DEFAULT_ADMIN_ROLE, _account); - } -} -`} - - -## Best Practices - - -- Initialize roles and grant administrative permissions during diamond deployment or upgrade to establish a secure baseline. -- Use `grantRoleBatch` and `revokeRoleBatch` for efficient management of multiple role assignments or revocations. -- Integrate role checks directly into facet functions using `requireRole` to enforce access control at the point of execution. - - -## Security Considerations - - -Ensure that the initial administrative roles are set correctly during deployment. Be mindful of the potential for denial-of-service if all roles are revoked from critical accounts. Access control checks are enforced by the facet itself; ensure functions requiring specific permissions correctly utilize `requireRole` or equivalent checks. - - -
- -
- - diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx deleted file mode 100644 index 78ce5b31..00000000 --- a/website/docs/library/access/AccessControl/AccessControlMod.mdx +++ /dev/null @@ -1,453 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlMod" -description: "Manages role-based access control within a diamond." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages role-based access control within a diamond. - - - -- Role-based access control for granular permission management. -- Functions to grant, revoke, and check roles for any account. -- Ability to set administrative roles for other roles, enabling hierarchical control. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The AccessControl module provides a robust system for managing roles and permissions within your Compose diamond. It allows for granular control over which accounts can perform specific actions by assigning them roles. This module is essential for building secure and auditable decentralized applications, ensuring that only authorized entities can execute sensitive functions. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns the storage for the AccessControl. - - -{`function getStorage() pure returns (AccessControlStorage storage _s);`} - - -**Returns:** - - - ---- -### grantRole - -function to grant a role to an account. - - -{`function grantRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### hasRole - -function to check if an account has a role. - - -{`function hasRole(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireRole - -function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - - -{`function requireRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeRole - -function to revoke a role from an account. - - -{`function revokeRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setRoleAdmin - -function to set the admin role for a role. - - -{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when the admin role for a role is changed. -
- -
- Signature: - -{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is granted to an account. -
- -
- Signature: - -{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a role is revoked from an account. -
- -
- Signature: - -{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlMod} from "@compose-protocol/diamond-contracts/contracts/modules/access-control/AccessControlMod.sol"; - -contract MyFacet { - IAccessControlMod internal immutable accessControl; - - constructor(address _diamondAddress) { - accessControl = IAccessControlMod(_diamondAddress); - } - - /** - * @notice Grants the DEFAULT_ADMIN_ROLE to the caller. - */ - function grantDefaultAdmin() external { - address caller = msg.sender; - bytes32 adminRole = accessControl.DEFAULT_ADMIN_ROLE(); // Assuming DEFAULT_ADMIN_ROLE is accessible or defined - accessControl.grantRole(adminRole, caller); - } - - /** - * @notice Checks if an account has a specific role. - * @param _role The role to check. - * @param _account The account to check. - * @return bool True if the account has the role, false otherwise. - */ - function checkRole(bytes32 _role, address _account) external view returns (bool) { - return accessControl.hasRole(_role, _account); - } -} -`} - - -## Best Practices - - -- Define custom roles and manage their administration carefully using `setRoleAdmin`. -- Use `requireRole` extensively to protect sensitive functions from unauthorized access. -- Ensure that the `DEFAULT_ADMIN_ROLE` is initially granted to a secure, multi-signature wallet or a trusted entity. - - -## Integration Notes - - -This module requires dedicated storage slots for its role mapping and role admin mapping. Facets can interact with this module by calling its external functions. The `hasRole` and `requireRole` functions provide immediate feedback on an account's permissions. Changes to role assignments or role administration are persistent and visible across all facets interacting with the diamond. - - -
- -
- - diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json deleted file mode 100644 index 1504700a..00000000 --- a/website/docs/library/access/AccessControl/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Access Control", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/access/AccessControl/index" - } -} diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx deleted file mode 100644 index 44eab9eb..00000000 --- a/website/docs/library/access/AccessControl/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Access Control" -description: "Role-based access control (RBAC) pattern." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Role-based access control (RBAC) pattern. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx deleted file mode 100644 index 62996189..00000000 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx +++ /dev/null @@ -1,332 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableFacet" -description: "Manage role pausing and access control within a diamond." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage role pausing and access control within a diamond. - - - -- Allows temporary suspension of specific roles. -- Integrates seamlessly with existing Compose access control mechanisms. -- Provides explicit revert reasons for unauthorized access and paused roles. - - -## Overview - -This facet provides granular control over role execution by allowing roles to be temporarily paused. It integrates with the diamond's access control system, ensuring that only authorized accounts can perform actions and that these actions can be suspended when a role is paused. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - -### State Variables - - - -## Functions - -### isRolePaused - -Returns if a role is paused. - - -{`function isRolePaused(bytes32 _role) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function pauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### unpauseRole - -Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function unpauseRole(bytes32 _role) external;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut, IDiamondLoupe} from "@compose/diamond-contracts/contracts/interfaces/IDiamond.sol"; - -import {AccessControlPausableFacet} from "@compose/diamond-contracts/contracts/facets/AccessControl/AccessControlPausableFacet.sol"; - -contract DeployDiamond { - // ... deployment setup ... - - function deploy() public { - // ... deploy other facets ... - - AccessControlPausableFacet accessControlPausableFacet = new AccessControlPausableFacet(); - - // Add AccessControlPausableFacet to the diamond - // ... diamond cut call ... - - // Example: Pausing a role - bytes32 pauseRoleSelector = AccessControlPausableFacet.pauseRole.selector; - // Assuming 'adminAccount' is the caller and has the admin role for the target role - (bool success, ) = address(diamond).call(abi.encodeWithSelector(pauseRoleSelector, _roleToPause)); - require(success, "Failed to pause role"); - - // Example: Checking if a role is paused - bytes4 isRolePausedSelector = AccessControlPausableFacet.isRolePaused.selector; - (success, ) = address(diamond).call(abi.encodeWithSelector(isRolePausedSelector, _roleToCheck)); - // ... check result ... - } -}`} - - -## Best Practices - - -- Integrate this facet into your diamond to add role-specific pausing capabilities to your access control. -- Ensure the caller has the appropriate administrative role before attempting to pause or unpause a role. -- Use `requireRoleNotPaused` within your facet functions that are subject to role pausing to enforce the active state of a role. - - -## Security Considerations - - -Access to `pauseRole` and `unpauseRole` is restricted to the administrator of the specific role, preventing unauthorized pausing or unpausing. The `requireRoleNotPaused` function ensures that calls to role-protected functions will revert if the role is currently paused, preventing unintended execution. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx deleted file mode 100644 index 5a102bb0..00000000 --- a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx +++ /dev/null @@ -1,377 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlPausableMod" -description: "Manage role-based access control with pause functionality." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage role-based access control with pause functionality. - - - -- Role-specific pausing: Allows granular control over which roles can execute functions. -- Pause/unpause functionality: Enables temporary suspension and resumption of role-based operations. -- Integrated access control checks: `requireRoleNotPaused` verifies both role membership and pause status. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module extends role-based access control by introducing pause functionality for specific roles. It allows administrators to temporarily halt operations associated with a role, enhancing safety and control during critical operations or upgrades. This composable module integrates seamlessly into the Compose diamond storage pattern. - ---- - -## Storage - -### AccessControlPausableStorage - - -{`struct AccessControlPausableStorage { - mapping(bytes32 role => bool paused) pausedRoles; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlPausable. - - -{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} - - -**Returns:** - - - ---- -### isRolePaused - -function to check if a role is paused. - - -{`function isRolePaused(bytes32 _role) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### pauseRole - -function to pause a role. - - -{`function pauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - ---- -### requireRoleNotPaused - -function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. - - -{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### unpauseRole - -function to unpause a role. - - -{`function unpauseRole(bytes32 _role) ;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is paused. -
- -
- Signature: - -{`event RolePaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a role is unpaused. -
- -
- Signature: - -{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role is paused and an operation requiring that role is attempted. -
- -
- Signature: - -error AccessControlRolePaused(bytes32 _role); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlPausableMod} from "@compose/modules/AccessControlPausableMod.sol"; - -contract MyFacet { - IAccessControlPausableMod internal accessControlPausableMod; - - function initialize(address _accessControlPausableModAddress) external { - accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableModAddress); - } - - function grantRoleAndPause(bytes32 _role) external { - // Assuming role granting is handled by another facet or module - // accessControlPausableMod.grantRole(_role, msg.sender); // Example, actual grant function may differ - accessControlPausableMod.pauseRole(_role); - } - - function performSensitiveOperation(bytes32 _role) external { - accessControlPausableMod.requireRoleNotPaused(_role, msg.sender); - // Proceed with operation - } - - function unpauseSensitiveRole(bytes32 _role) external { - accessControlPausableMod.unpauseRole(_role); - } -}`} - - -## Best Practices - - -- Ensure `requireRoleNotPaused` is called before critical operations to prevent execution when a role is paused. -- Implement role management (granting/revoking) in a separate, dedicated facet for clarity and separation of concerns. -- Use custom errors `AccessControlRolePaused` and `AccessControlUnauthorizedAccount` for clear revert reasons. - - -## Integration Notes - - -The `AccessControlPausableMod` module manages its state within its own storage slots, separate from other facets. Facets interacting with this module must obtain its address and call its functions. The `requireRoleNotPaused` function enforces invariants by reverting if the calling account lacks the specified role or if the role is currently paused. The module's storage is accessible via `getAccessControlStorage` and `getStorage` for auditing or debugging purposes, but direct modification is not recommended. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json deleted file mode 100644 index 96418b00..00000000 --- a/website/docs/library/access/AccessControlPausable/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Pausable Access Control", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/access/AccessControlPausable/index" - } -} diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx deleted file mode 100644 index 0e056cb6..00000000 --- a/website/docs/library/access/AccessControlPausable/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Pausable Access Control" -description: "RBAC with pause functionality." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - RBAC with pause functionality. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx deleted file mode 100644 index 783d6256..00000000 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx +++ /dev/null @@ -1,404 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalFacet" -description: "Manages time-bound role assignments and checks for access control." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages time-bound role assignments and checks for access control. - - - -- Grants roles with specific expiration timestamps. -- Provides functions to check if a role assignment has expired. -- Enforces role validity, considering both existence and expiry. -- Role granting and revocation are restricted to the role's admin. - - -## Overview - -This facet extends Compose's access control by introducing time-bound role assignments. It allows for granting roles that automatically expire and provides mechanisms to check for role validity, including expiry status. This enables dynamic access control policies based on time. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getRoleExpiry - -Returns the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -Checks if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) external;`} - - -**Parameters:** - - - ---- -### requireValidRole - -Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( - bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {AccessControlTemporalFacet} from "@compose/access-control/facets/AccessControlTemporalFacet.sol"; - -contract MyDiamond { - // Assume AccessControlFacet and diamond deploy logic are present - - function grantRoleWithExpiry(bytes32 role, address account, uint64 expiry) external { - AccessControlTemporalFacet(diamondProxyAddress).grantRoleWithExpiry(role, account, expiry); - } - - function revokeTemporalRole(bytes32 role, address account) external { - AccessControlTemporalFacet(diamondProxyAddress).revokeTemporalRole(role, account); - } - - function isRoleExpired(bytes32 role, address account) external view returns (bool) { - return AccessControlTemporalFacet(diamondProxyAddress).isRoleExpired(role, account); - } - - function requireValidRole(bytes32 role, address account) external view { - AccessControlTemporalFacet(diamondProxyAddress).requireValidRole(role, account); - } -}`} - - -## Best Practices - - -- Use `grantRoleWithExpiry` to assign roles with a predefined expiration. Ensure the caller has the necessary administrative privileges for the target role. -- Utilize `isRoleExpired` or `requireValidRole` to enforce time-sensitive access control checks within your application logic. -- Integrate this facet carefully to manage the lifecycle of roles that should not be permanent. - - -## Security Considerations - - -Access to `grantRoleWithExpiry` and `revokeTemporalRole` is restricted to the administrative role for the specified role, preventing unauthorized role management. The `requireValidRole` function correctly reverts with `AccessControlRoleExpired` if a role has passed its expiry, ensuring that expired access is denied. Ensure the `expiry` timestamp is set correctly to avoid unintended access durations. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx deleted file mode 100644 index 7945c0dd..00000000 --- a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx +++ /dev/null @@ -1,479 +0,0 @@ ---- -sidebar_position: 99 -title: "AccessControlTemporalMod" -description: "Manage roles with time-bound access control." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage roles with time-bound access control. - - - -- Grants roles with specific expiry timestamps. -- Automatically revokes expired roles, enforcing time-limited access. -- Provides functions to check role validity and expiry status. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module introduces time-bound access control, allowing roles to be granted with an expiry timestamp. It ensures that access is automatically revoked once the specified expiry is reached, enhancing security and simplifying access management for time-sensitive permissions within a diamond. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; - mapping(bytes32 role => bytes32 adminRole) adminRole; -}`} - - ---- -### AccessControlTemporalStorage - - -{`struct AccessControlTemporalStorage { - mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; -}`} - - -### State Variables - - - -## Functions - -### getAccessControlStorage - -Returns the storage for AccessControl. - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - -**Returns:** - - - ---- -### getRoleExpiry - -function to get the expiry timestamp for a role assignment. - - -{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getStorage - -Returns the storage for AccessControlTemporal. - - -{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} - - -**Returns:** - - - ---- -### grantRoleWithExpiry - -function to grant a role with an expiry timestamp. - - -{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isRoleExpired - -function to check if a role assignment has expired. - - -{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### requireValidRole - -function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. - - -{`function requireValidRole(bytes32 _role, address _account) view;`} - - -**Parameters:** - - - ---- -### revokeTemporalRole - -function to revoke a temporal role. - - -{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Event emitted when a role is granted with an expiry timestamp. -
- -
- Signature: - -{`event RoleGrantedWithExpiry( -bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender -);`} - -
- -
- Parameters: - -
-
- -
- Event emitted when a temporal role is revoked. -
- -
- Signature: - -{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a role has expired. -
- -
- Signature: - -error AccessControlRoleExpired(bytes32 _role, address _account); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IAccessControlTemporalMod} from "@compose/contracts/modules/access/AccessControlTemporalMod.sol"; - -contract MyFacet { - IAccessControlTemporalMod private constant _ACCESS_CONTROL_TEMPORAL = IAccessControlTemporalMod(address(this)); // Replace with actual diamond address - - function _performActionWithRole(address _account, bytes32 _role) internal { - // Check for valid and non-expired role - _ACCESS_CONTROL_TEMPORAL.requireValidRole(_account, _role); - - // Proceed with action if role is valid - // ... - } - - function _grantTemporaryRole(address _account, bytes32 _role, uint64 _expiry) external { - // Grant role with expiry - _ACCESS_CONTROL_TEMPORAL.grantRoleWithExpiry(_account, _role, _expiry); - } - - function _revokeRole(address _account, bytes32 _role) external { - // Revoke temporal role - _ACCESS_CONTROL_TEMPORAL.revokeTemporalRole(_account, _role); - } -}`} - - -## Best Practices - - -- Use `requireValidRole` to enforce time-bound access control before executing sensitive operations. -- Ensure expiry timestamps are set appropriately to prevent indefinite access and manage role lifecycles effectively. -- Handle `AccessControlRoleExpired` and `AccessControlUnauthorizedAccount` errors to provide clear feedback to users. - - -## Integration Notes - - -The `AccessControlTemporalMod` interacts with diamond storage, requiring the presence of the underlying access control and temporal access control storage structures. Facets calling its functions will see the updated role assignments and expiry statuses. Ensure the module is initialized and correctly integrated into the diamond's facet registry. The `requireValidRole` function directly enforces access control logic, reverting if a role is unauthorized or expired. - - -
- -
- - diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json deleted file mode 100644 index 834b0b18..00000000 --- a/website/docs/library/access/AccessControlTemporal/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Temporal Access Control", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/access/AccessControlTemporal/index" - } -} diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx deleted file mode 100644 index 9e92809c..00000000 --- a/website/docs/library/access/AccessControlTemporal/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Temporal Access Control" -description: "Time-limited role-based access control." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Time-limited role-based access control. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx deleted file mode 100644 index 0944dd54..00000000 --- a/website/docs/library/access/Owner/OwnerFacet.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerFacet" -description: "Manages diamond ownership and transfers." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages diamond ownership and transfers. - - - -- Provides owner address retrieval. -- Supports ownership transfer to a new address. -- Allows for the renunciation of ownership. - - -## Overview - -The OwnerFacet provides essential functions for managing the ownership of a Compose diamond. It allows querying the current owner, transferring ownership to a new address, and renouncing ownership entirely. This facet is crucial for controlling administrative actions within the diamond. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - -### State Variables - - - -## Functions - -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerFacet} from "../interfaces/IOwnerFacet.sol"; - -contract Deployer { - // Assume diamondAbi is the ABI of your diamond - // Assume diamondAddress is the address of your deployed diamond - IOwnerFacet ownerFacet = IOwnerFacet(diamondAddress); - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function transferDiamondOwnership(address _newOwner) external { - // Ensure you have the necessary permissions to call this function - ownerFacet.transferOwnership(_newOwner); - } - - function renounceDiamondOwnership() external { - // Ensure you have the necessary permissions to call this function - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Use `transferOwnership` to safely delegate control by setting a new owner. -- Call `renounceOwnership` with extreme caution, as it makes the diamond effectively un-administerable. -- Ensure that only authorized entities can call `transferOwnership` and `renounceOwnership`. - - -## Security Considerations - - -Ownership transfer functions (`transferOwnership`, `renounceOwnership`) are highly sensitive. Ensure that calls to these functions are restricted to the current owner or a designated administrative role to prevent unauthorized control of the diamond. The `transferOwnership` function allows setting the new owner to `address(0)` to effectively renounce ownership, which should be handled with care. - - -
- -
- - diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx deleted file mode 100644 index dcea3566..00000000 --- a/website/docs/library/access/Owner/OwnerMod.mdx +++ /dev/null @@ -1,254 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerMod" -description: "Manages ERC-173 contract ownership and access control." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-173 contract ownership and access control. - - - -- ERC-173 compliant ownership tracking. -- Functions for retrieving, transferring, and renouncing ownership. -- Access control enforcement via `requireOwner`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The OwnerMod provides essential functionality for managing contract ownership according to the ERC-173 standard. It defines storage for the owner's address and offers functions to retrieve, transfer, and enforce ownership, crucial for secure administrative operations within a Compose diamond. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { - address owner; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Get the address of the owner - - -{`function owner() view returns (address);`} - - -**Returns:** - - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### setContractOwner - - -{`function setContractOwner(address _initialOwner) ;`} - - -**Parameters:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- This emits when ownership of a contract changes. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerMod} from "@compose/modules/OwnerMod/IOwnerMod.sol"; -import {OwnerStorage} from "@compose/modules/OwnerMod/OwnerStorage.sol"; - -contract MyOwnerFacet { - uint256 constant OWNER_STORAGE_POSITION = 1; // Example storage slot - - function getOwner() public view returns (address) { - // Access storage directly or via a helper if provided by the module - // This example assumes direct access to storage for demonstration - OwnerStorage storage ownerStorage = OwnerStorage(OWNER_STORAGE_POSITION); - return ownerStorage.owner; - } - - function transferOwner(address _newOwner) external { - // Assuming OwnerMod provides an interface or direct access - // This is a conceptual call, actual implementation depends on module's facet interface - IOwnerMod(OWNER_STORAGE_POSITION).transferOwnership(_newOwner); - } - - function requireIsOwner() external view { - // Assuming OwnerMod provides an interface or direct access - IOwnerMod(OWNER_STORAGE_POSITION).requireOwner(); - } -}`} - - -## Best Practices - - -- Use `transferOwnership` to safely transfer ownership, setting the new owner's address. -- Set the owner to `address(0)` to renounce ownership when necessary, making the contract permissionless. -- Utilize `requireOwner` within administrative functions to enforce access control and prevent unauthorized actions. - - -## Integration Notes - - -The OwnerMod reserves a specific storage slot (defined by `STORAGE_POSITION`) for its `OwnerStorage` struct. Facets interacting with ownership should access this storage slot. Changes to the owner address are immediately visible to all facets through this shared storage. The `setContractOwner` function is intended for initial deployment or specific administrative scenarios, while `transferOwnership` is the standard method for ongoing ownership management. - - -
- -
- - diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json deleted file mode 100644 index 2ddf56c9..00000000 --- a/website/docs/library/access/Owner/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Owner", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/access/Owner/index" - } -} diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx deleted file mode 100644 index 4ecc8496..00000000 --- a/website/docs/library/access/Owner/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Owner" -description: "Single-owner access control pattern." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Single-owner access control pattern. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx deleted file mode 100644 index aa14912c..00000000 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx +++ /dev/null @@ -1,246 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsFacet" -description: "Manages contract ownership with a two-step transfer process." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages contract ownership with a two-step transfer process. - - - -- Two-step ownership transfer process for enhanced security. -- Explicit `acceptOwnership` call by the new owner. -- `renounceOwnership` function to remove ownership. -- Direct storage access via inline assembly for efficiency. - - -## Overview - -This facet provides a secure, two-step ownership transfer mechanism, preventing accidental or malicious takeover of the diamond. It allows the current owner to initiate a transfer and requires the new owner to accept it, ensuring explicit confirmation. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### owner - -Get the address of the owner - - -{`function owner() external view returns (address);`} - - -**Returns:** - - - ---- -### pendingOwner - -Get the address of the pending owner - - -{`function pendingOwner() external view returns (address);`} - - -**Returns:** - - - ---- -### transferOwnership - -Set the address of the new owner of the contract - - -{`function transferOwnership(address _newOwner) external;`} - - -**Parameters:** - - - ---- -### acceptOwnership - - -{`function acceptOwnership() external;`} - - ---- -### renounceOwnership - - -{`function renounceOwnership() external;`} - - -## Events - - - - -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- - -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoStepsFacet} from "@compose/contracts/src/facets/owner/IOwnerTwoStepsFacet.sol"; - -contract OwnerConsumer { - IOwnerTwoStepsFacet public ownerFacet; - - constructor(address _ownerFacetAddress) { - ownerFacet = IOwnerTwoStepsFacet(_ownerFacetAddress); - } - - function initiateOwnershipTransfer(address _newOwner) external { - // Assumes caller is the current owner - ownerFacet.transferOwnership(_newOwner); - } - - function acceptNewOwnership() external { - // Assumes caller is the pending owner - ownerFacet.acceptOwnership(); - } - - function renounceContractOwnership() external { - // Assumes caller is the current owner - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Only the current owner should call `transferOwnership` and `renounceOwnership`. -- Only the pending owner should call `acceptOwnership`. -- Store the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` constants securely and ensure they are correctly set during deployment. - - -## Security Considerations - - -Ensure that the `transferOwnership` function is only callable by the current owner. The `acceptOwnership` function must only be callable by the pending owner. Incorrect access control could lead to unauthorized ownership changes. The inline assembly for storage access relies on the correct definition and immutability of `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION`; any change to these slots could break ownership management. - - -
- -
- - diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx deleted file mode 100644 index 361c4d3d..00000000 --- a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx +++ /dev/null @@ -1,303 +0,0 @@ ---- -sidebar_position: 99 -title: "OwnerTwoStepsMod" -description: "Manages ERC-173 contract ownership with a two-step transfer process." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-173 contract ownership with a two-step transfer process. - - - -- Implements a secure two-step ownership transfer process. -- Provides functions to view current and pending owners. -- Includes a `requireOwner` guard for owner-only operations. -- Supports `renounceOwnership` to disable owner-specific functionality. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides a secure, two-step ownership transfer mechanism compliant with ERC-173. It ensures that ownership changes can only be finalized by the intended new owner, preventing accidental or malicious transfers. This pattern is crucial for managing administrative functions within a diamond proxy. - ---- - -## Storage - -### OwnerStorage - -storage-location: erc8042:compose.owner - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### PendingOwnerStorage - -storage-location: erc8042:compose.owner.pending - - -{`struct PendingOwnerStorage { - address pendingOwner; -}`} - - -### State Variables - - - -## Functions - -### acceptOwnership - -Finalizes ownership transfer; must be called by the pending owner. - - -{`function acceptOwnership() ;`} - - ---- -### getOwnerStorage - -Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. - - -{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} - - -**Returns:** - - - ---- -### getPendingOwnerStorage - -Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. - - -{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} - - -**Returns:** - - - ---- -### owner - -Returns the current owner. - - -{`function owner() view returns (address);`} - - ---- -### pendingOwner - -Returns the pending owner (if any). - - -{`function pendingOwner() view returns (address);`} - - ---- -### renounceOwnership - -Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. - - -{`function renounceOwnership() ;`} - - ---- -### requireOwner - -Reverts if the caller is not the owner. - - -{`function requireOwner() view;`} - - ---- -### transferOwnership - -Initiates a two-step ownership transfer. - - -{`function transferOwnership(address _newOwner) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership transfer is initiated (pending owner set). -
- -
- Signature: - -{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
- -
- Emitted when ownership transfer is finalized. -
- -
- Signature: - -{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerAlreadyRenounced(); - -
-
- - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IOwnerTwoSteps} from "@compose/contracts/modules/owner/IOwnerTwoSteps.sol"; - -contract MyOwnerFacet { - IOwnerTwoSteps public ownerFacet; - - // Assuming ownerFacet is initialized elsewhere and points to the OwnerTwoStepsMod - - function transferAdmin(address _newOwner) external { - ownerFacet.transferOwnership(_newOwner); - } - - function acceptAdmin() external { - ownerFacet.acceptOwnership(); - } - - function getCurrentOwner() external view returns (address) { - return ownerFacet.owner(); - } - - function getCurrentPendingOwner() external view returns (address) { - return ownerFacet.pendingOwner(); - } - - function renounceAdmin() external { - ownerFacet.renounceOwnership(); - } -}`} - - -## Best Practices - - -- Use `transferOwnership` to initiate a transfer, followed by `acceptOwnership` by the new owner. -- Protect critical administrative functions using `requireOwner` or by checking the `owner()` return value. -- Be aware that `renounceOwnership` permanently relinquishes ownership, setting the owner to `address(0)`. - - -## Integration Notes - - -The `OwnerTwoStepsMod` stores ownership data in dedicated storage slots. Facets interacting with this module should use the provided accessor functions (`owner`, `pendingOwner`) to retrieve ownership information. The module's storage layout is fixed and should not be altered by other facets to maintain invariant integrity. - - -
- -
- - diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json deleted file mode 100644 index 90b66a92..00000000 --- a/website/docs/library/access/OwnerTwoSteps/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Two-Step Owner", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/access/OwnerTwoSteps/index" - } -} diff --git a/website/docs/library/access/OwnerTwoSteps/index.mdx b/website/docs/library/access/OwnerTwoSteps/index.mdx deleted file mode 100644 index a02ceef6..00000000 --- a/website/docs/library/access/OwnerTwoSteps/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Two-Step Owner" -description: "Two-step ownership transfer pattern." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Two-step ownership transfer pattern. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json deleted file mode 100644 index cbc9d5ba..00000000 --- a/website/docs/library/access/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Access Control", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/access/index" - } -} diff --git a/website/docs/library/access/index.mdx b/website/docs/library/access/index.mdx deleted file mode 100644 index edf619c1..00000000 --- a/website/docs/library/access/index.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: "Access Control" -description: "Access control patterns for permission management in Compose diamonds." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Access control patterns for permission management in Compose diamonds. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx deleted file mode 100644 index 9272f3b1..00000000 --- a/website/docs/library/diamond/DiamondCutFacet.mdx +++ /dev/null @@ -1,323 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutFacet" -description: "Manage diamond facets and functions" -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and functions - - - -- Atomic addition, replacement, and removal of facets and functions. -- Supports executing an initialization function after a diamond cut. -- Provides access to diamond storage related to facet management. - - -## Overview - -The DiamondCutFacet provides essential functions for managing the diamond's upgradeability and function routing. It allows for the addition, replacement, and removal of facets and their associated functions, along with optional initialization execution. This facet is central to the Compose diamond's dynamic nature and extensibility. - ---- - -## Storage - -### OwnerStorage - - -{`struct OwnerStorage { - address owner; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error OwnerUnauthorizedAccount(); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutFacet} from "@compose-protocol/diamond-contracts/facets/DiamondCutFacet.sol"; -import {IDiamondCut} from "@compose-protocol/diamond-contracts/interfaces/IDiamondCut.sol"; - -contract DeployDiamond { - // Assume diamondProxy is already deployed and initialized - address diamondProxy; - - function upgradeDiamond() public { - DiamondCutFacet diamondCutFacet = DiamondCutFacet(diamondProxy); - - // Example: Add a new facet - bytes memory facetImplementation = type(MyNewFacet).creationCode; - address facetAddress = address(new MyNewFacet()); // Or deploy and get address - - // Call addFunctions to add the new facet and its functions - diamondCutFacet.diamondCut( - IDiamondCut.FacetCut[]( - IDiamondCut.FacetCut( - facetAddress, - IDiamondCut.FacetCutAction.ADD, - bytes4(keccak256("myNewFunction() -")) // Selector for myNewFunction - ) - ), - address(0), // Init address - "" // Init calldata - ); - } - - // Example of a custom facet - contract MyNewFacet { - function myNewFunction() external pure { - // ... implementation ... - } - } -}`} - - -## Best Practices - - -- Ensure that only authorized addresses can call `diamondCut` functions. This typically involves access control mechanisms within the diamond's ownership or governance setup. -- When replacing or removing functions, carefully consider the potential impact on existing interactions with the diamond to avoid breaking changes for consumers. -- Use the `diamondCut` function to add, replace, or remove multiple functions atomically. This ensures that the diamond's function map remains consistent. - - -## Security Considerations - - -The `diamondCut` function is a powerful administrative function. Unauthorized access could lead to the diamond being rendered inoperable or its functionality being compromised. Ensure robust access control is implemented for this function. Reentrancy is not a direct concern for `diamondCut` itself, but any initialization function called via `diamondCut` must be reentrancy-guarded if it interacts with external contracts or modifies state that could be re-entered. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx deleted file mode 100644 index 65f69a56..00000000 --- a/website/docs/library/diamond/DiamondCutMod.mdx +++ /dev/null @@ -1,375 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondCutMod" -description: "Manage diamond facets and function registrations." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage diamond facets and function registrations. - - - -- Atomic `diamondCut` for adding, replacing, and removing multiple functions in a single transaction. -- Supports delegatecalling an initialization function during a diamond cut operation. -- Emits a `DiamondCut` event upon successful execution of facet modifications. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondCutMod provides essential functions for managing the diamond's facets. It allows for the addition, replacement, and removal of function selectors, enabling dynamic upgrades and modifications to the diamond's capabilities. This module is crucial for maintaining and evolving the diamond's functionality post-deployment. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond - */ - bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFunctions - - -{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### diamondCut - -Add/replace/remove any number of functions and optionally execute a function with delegatecall - - -{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} - - -**Parameters:** - - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - ---- -### removeFunctions - - -{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - ---- -### replaceFunctions - - -{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotRemoveImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); - -
-
- - -
- Signature: - -error CannotReplaceImmutableFunction(bytes4 _selector); - -
-
- - -
- Signature: - -error IncorrectFacetCutAction(uint8 _action); - -
-
- - -
- Signature: - -error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
- - -
- Signature: - -error NoSelectorsProvidedForFacet(address _facet); - -
-
- - -
- Signature: - -error RemoveFacetAddressMustBeZeroAddress(address _facet); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondCutMod} from "@compose/diamond-proxy/modules/diamond-cut/DiamondCutMod.sol"; -import {IDiamondCut} from "@compose/diamond-proxy/interfaces/IDiamondCut.sol"; - -contract MyFacet { - // Assume DiamondCutMod is already added to the diamond - - function upgradeDiamond(address _diamondCutAddress) external { - // Example: Add a new function - IDiamondCut(_diamondCutAddress).addFunctions( - address(this), - bytes4[](uint256(0), IDiamondCut.myNewFunction.selector) - ); - } - - function myNewFunction() external pure { - // ... implementation ... - } -}`} - - -## Best Practices - - -- Use `diamondCut` for all facet and function management operations to ensure atomicity and proper event emission. -- Be aware of the `CannotRemoveImmutableFunction` error and ensure you do not attempt to remove functions marked as immutable. -- Thoroughly test facet additions and removals in a staging environment before applying to production diamonds. - - -## Integration Notes - - -The DiamondCutMod interacts directly with the diamond's storage to register and unregister function selectors. Facets added or modified through this module become immediately available via the diamond proxy. Ensure that the DiamondCutMod is initialized correctly and that its storage slot is not conflicted with by other modules. The order of operations within a single `diamondCut` call is important for managing dependencies between facet additions and removals. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx deleted file mode 100644 index d539c37e..00000000 --- a/website/docs/library/diamond/DiamondInspectFacet.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondInspectFacet" -description: "Inspect diamond storage and facet mappings." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondInspectFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Inspect diamond storage and facet mappings. - - - -- Directly accesses diamond storage via inline assembly. -- Provides a comprehensive mapping of function selectors to facet addresses. -- Read-only operations, ensuring no state modification. - - -## Overview - -The DiamondInspectFacet provides essential read-only capabilities for understanding a Compose diamond's internal state and function distribution. It allows developers to query the diamond's core storage structure and map function selectors to their respective implementation facets. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### FunctionFacetPair - - -{`struct FunctionFacetPair { - bytes4 selector; - address facet; -}`} - - -### State Variables - - - -## Functions - -### functionFacetPairs - -Returns an array of all function selectors and their corresponding facet addresses. Iterates through the diamond's stored selectors and pairs each with its facet. - - -{`function functionFacetPairs() external view returns (FunctionFacetPair[] memory pairs);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondInspect} from "@compose/contracts/facets/DiamondInspect/IDiamondInspect.sol"; - -contract Consumer { - IDiamondInspect immutable diamondInspect; - - constructor(address _diamondAddress) { - diamondInspect = IDiamondInspect(_diamondAddress); - } - - function inspectStorage() public view returns (bytes memory) { - return diamondInspect.getStorage(); - } - - function inspectFunctionMappings() public view returns (struct IDiamondInspect.FunctionFacetPair[] memory) { - return diamondInspect.functionFacetPairs(); - } -}`} - - -## Best Practices - - -- Integrate this facet to audit diamond state and function assignments. -- Use `getStorage()` cautiously; its output is a raw storage slot and requires understanding of the diamond's storage layout. -- `functionFacetPairs()` is invaluable for debugging and understanding runtime dispatch. - - -## Security Considerations - - -The `getStorage()` function returns raw storage data. Misinterpretation or misuse of this data could lead to incorrect assumptions about the diamond's state. Ensure that any logic relying on `getStorage()` is robust and accounts for potential storage layout changes during upgrades. `functionFacetPairs()` is generally safe, but the addresses returned should be validated if used in sensitive operations. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx deleted file mode 100644 index 55b39881..00000000 --- a/website/docs/library/diamond/DiamondLoupeFacet.mdx +++ /dev/null @@ -1,249 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondLoupeFacet" -description: "Inspect diamond facets, functions, and storage locations." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondLoupeFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Inspect diamond facets, functions, and storage locations. - - - -- Provides read-only access to diamond's facet and function mappings. -- Optimized for gas efficiency when querying large numbers of facets and selectors. -- Supports querying for specific facet addresses or all deployed facet addresses. - - -## Overview - -The DiamondLoupeFacet provides essential introspection capabilities for a diamond proxy. It allows developers to query which facets are deployed, what functions each facet supports, and the addresses of these facets. This facet is crucial for understanding the diamond's internal structure and for dynamic contract interactions. - ---- - -## Storage - -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### DiamondStorage - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * Array of all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### Facet - - -{`struct Facet { - address facet; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### facetAddress - -Gets the facet address that supports the given selector. If facet is not found return address(0). - - -{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetFunctionSelectors - -Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. - - -{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### facetAddresses - -Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. - - -{`function facetAddresses() external view returns (address[] memory allFacets);`} - - -**Returns:** - - - ---- -### facets - -Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. - - -{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose/diamond-facets/DiamondLoupeFacet.sol"; - -contract MyDiamond { - DiamondLoupeFacet public diamondLoupeFacet; - - function initDiamond(address[] memory _facetAddresses, bytes4[][] memory _facetSelectors) external { - // ... deployment logic ... - diamondLoupeFacet = DiamondLoupeFacet(address(this)); - } - - // Example of calling a function from DiamondLoupeFacet - function getFunctionSelectors(address _facetAddress) external view returns (bytes4[] memory) { - return diamondLoupeFacet.facetFunctionSelectors(_facetAddress); - } - - function getAllFacets() external view returns ( - address[] memory facetAddresses, - address[] memory facetAddresses, - bytes4[][] memory facetSelectors - ) { - return diamondLoupeFacet.facets(); - } -}`} - - -## Best Practices - - -- Deploy DiamondLoupeFacet as part of the initial diamond deployment to ensure introspection capabilities are available from the start. -- Use the `facets` function to get a comprehensive view of the diamond's composition during development and auditing. -- Cache facet addresses and selectors in off-chain applications for performance, rather than repeatedly querying the diamond. - - -## Security Considerations - - -This facet is read-only and does not modify state, posing minimal direct security risks. However, the information it provides can be used to identify potential attack vectors if not properly secured by other facets. Ensure that access control for state-modifying functions is handled by the respective facets, not this introspection facet. - - -
- -
- - diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx deleted file mode 100644 index 7e82c958..00000000 --- a/website/docs/library/diamond/DiamondMod.mdx +++ /dev/null @@ -1,234 +0,0 @@ ---- -sidebar_position: 99 -title: "DiamondMod" -description: "Manages diamond proxy facets and internal state." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages diamond proxy facets and internal state. - - - -- Supports adding facets and their selectors exclusively during diamond deployment. -- Implements a fallback mechanism to route function calls to the appropriate facet. -- Provides access to the diamond's internal storage layout. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The DiamondMod module provides essential internal functions for managing facets within a Compose diamond. It handles adding new facets during deployment and enables the diamond's fallback mechanism to route external calls to the correct facet, ensuring composability and extensibility. - ---- - -## Storage - -### FacetCutAction - -Add=0, Replace=1, Remove=2 - ---- -### DiamondStorage - -storage-location: erc8042:compose.diamond - - -{`struct DiamondStorage { - mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; - /** - * \`selectors\` contains all function selectors that can be called in the diamond. - */ - bytes4[] selectors; -}`} - - ---- -### FacetAndPosition - - -{`struct FacetAndPosition { - address facet; - uint32 position; -}`} - - ---- -### FacetCut - - -{`struct FacetCut { - address facetAddress; - FacetCutAction action; - bytes4[] functionSelectors; -}`} - - -### State Variables - - - -## Functions - -### addFacets - -Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. - - -{`function addFacets(FacetCut[] memory _facets) ;`} - - -**Parameters:** - - - ---- -### diamondFallback - -Find facet for function that is called and execute the function if a facet is found and return any value. - - -{`function diamondFallback() ;`} - - ---- -### getStorage - - -{`function getStorage() pure returns (DiamondStorage storage s);`} - - -## Events - - - - -
- Signature: - -{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); - -
-
- - -
- Signature: - -error FunctionNotFound(bytes4 _selector); - -
-
- - -
- Signature: - -error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); - -
-
- - -
- Signature: - -error NoBytecodeAtAddress(address _contractAddress, string _message); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondMod} from "@compose/diamond-proxy/src/diamond-proxy/IDiamondMod.sol"; - -contract MyFacet { - IDiamondMod internal diamondMod; - - constructor(address _diamondMod) { - diamondMod = IDiamondMod(_diamondMod); - } - - /** - * @notice Example of calling getStorage. - */ - function readInternalStorage() external view returns (bytes memory) { - return diamondMod.getStorage(); - } -}`} - - -## Best Practices - - -- Facet addition is restricted to the initial diamond deployment phase to maintain consistency. -- Utilize `diamondFallback` for internal call routing; ensure function selectors are correctly registered. -- Handle potential errors like `FunctionNotFound` and `CannotAddFunctionToDiamondThatAlreadyExists`. - - -## Integration Notes - - -DiamondMod interacts directly with the diamond's storage contract. The `addFacets` function is intended for use only during the initial deployment of a diamond to register facets and their associated function selectors. The `diamondFallback` function reads the facet cut information from storage to determine the correct facet for executing incoming calls. `getStorage` returns the raw storage slot of the diamond's internal state. - - -
- -
- - diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json deleted file mode 100644 index 26c8cc37..00000000 --- a/website/docs/library/diamond/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Diamond Core", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/diamond/index" - } -} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx deleted file mode 100644 index 86ec8496..00000000 --- a/website/docs/library/diamond/example/ExampleDiamond.mdx +++ /dev/null @@ -1,128 +0,0 @@ ---- -sidebar_position: 99 -title: "ExampleDiamond" -description: "Example Diamond for Compose, demonstrating core proxy functionality." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/example/ExampleDiamond.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Example Diamond for Compose, demonstrating core proxy functionality. - - - -- Core diamond proxy functionality for facet routing. -- Initializes diamond with owner and facet mappings. -- Demonstrates basic facet registration and ownership setup. - - -## Overview - -ExampleDiamond serves as a foundational implementation for Compose diamonds. It showcases the basic proxy logic for routing function calls to registered facets and includes essential initialization capabilities for setting up diamond ownership and facet mappings. - ---- - -## Storage - -## Functions - -### constructor - -Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. - - -{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} - - -**Parameters:** - - - ---- -### fallback - - -{`fallback() external payable;`} - - ---- -### receive - - -{`receive() external payable;`} - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IDiamondCut, IDiamondLoupe} from "@compose-diamond/diamond-contracts/contracts/interfaces/IDiamond.sol"; -import {ExampleDiamond} from "@compose-diamond/diamond-contracts/contracts/ExampleDiamond.sol"; - -contract Deployer { - address owner = msg.sender; - - function deploy() public { - // Example facets (replace with actual facet addresses and selectors) - address[] memory facetAddresses = new address[](1); - facetAddresses[0] = address(0x123); // Replace with actual facet address - - bytes4[][] memory facetSelectors = new bytes4[][](1); - facetSelectors[0] = new bytes4[](1); - facetSelectors[0][0] = ExampleDiamond.exampleFunction.selector; // Replace with actual function selector - - ExampleDiamond diamond = new ExampleDiamond(facetAddresses, facetSelectors, owner); - - // Interactions with the deployed diamond would follow here - } -}`} - - -## Best Practices - - -- Initialize the diamond with the owner and all initial facets during deployment. -- Ensure all facets intended for use are correctly registered with their function selectors. -- Use the `IDiamondCut` interface for managing facet additions, replacements, or removals in upgrade scenarios. - - -## Security Considerations - - -Access control for diamond upgrades (add/replace/remove facets) is typically managed by the owner. Ensure the owner address is secure. The `fallback` and `receive` functions are present but have no specific logic defined in this example, relying on the diamond proxy's default behavior. - - -
- -
- - diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json deleted file mode 100644 index 8e4d0ed5..00000000 --- a/website/docs/library/diamond/example/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "example", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/diamond/example/index" - } -} diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx deleted file mode 100644 index 769767b6..00000000 --- a/website/docs/library/diamond/example/index.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "example" -description: "example components for Compose diamonds." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - example components for Compose diamonds. - - - - } - size="medium" - /> - diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx deleted file mode 100644 index 9ce67b22..00000000 --- a/website/docs/library/diamond/index.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: "Diamond Core" -description: "Core diamond proxy functionality for ERC-2535 diamonds." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Core diamond proxy functionality for ERC-2535 diamonds. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx deleted file mode 100644 index 8de81297..00000000 --- a/website/docs/library/index.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: "Library" -description: "API reference for all Compose modules and facets." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - API reference for all Compose modules and facets. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx deleted file mode 100644 index 4615d6fd..00000000 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx +++ /dev/null @@ -1,140 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Facet" -description: "Implements the ERC-165 interface detection standard." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements the ERC-165 interface detection standard. - - - -- Implements EIP-165 standard for interface detection. -- Allows querying supported interfaces via `supportsInterface` function. -- Provides access to ERC-165 storage via `getStorage`. - - -## Overview - -The ERC165Facet provides standard interface detection for a Compose diamond. It allows external contracts to query which ERC-165 supported interfaces the diamond implements, enhancing composability and interoperability. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { - /** - * @notice Mapping of interface IDs to whether they are supported - */ - mapping(bytes4 => bool) supportedInterfaces; -}`} - - -### State Variables - - - -## Functions - -### supportsInterface - -Query if a contract implements an interface This function checks if the diamond supports the given interface ID - - -{`function supportsInterface(bytes4 _interfaceId) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC165Facet} from "@compose/diamond/facets/ERC165/IERC165Facet.sol"; - -contract Consumer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function isERC721Supported() public view returns (bool) { - // ERC721 interface ID - bytes4 interfaceId = 0x80ac58cd; - return IERC165Facet(diamondAddress).supportsInterface(interfaceId); - } -}`} - - -## Best Practices - - -- Ensure the `ERC165Facet` is initialized with correct supported interfaces during diamond deployment. -- Use `supportsInterface` to verify compatibility before interacting with diamond functions that rely on specific interfaces. - - -## Security Considerations - - -This facet is read-only for external callers, posing minimal reentrancy risks. Ensure the `supportedInterfaces` mapping is correctly populated during initialization to prevent false positives or negatives regarding interface support. - - -
- -
- - diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx deleted file mode 100644 index cd09d88a..00000000 --- a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC165Mod" -description: "Implements ERC-165 interface detection for diamonds." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements ERC-165 interface detection for diamonds. - - - -- Provides standard ERC-165 interface detection capabilities. -- Facilitates compliance with the ERC-165 standard for diamond proxies. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC165Mod provides the necessary internal functions and storage layout to support ERC-165 interface detection within a Compose diamond. By registering supported interfaces during facet initialization, diamonds can correctly report their capabilities to external callers, ensuring compliance with the ERC-165 standard. - ---- - -## Storage - -### ERC165Storage - - -{`struct ERC165Storage { - /* - * @notice Mapping of interface IDs to whether they are supported - */ - mapping(bytes4 => bool) supportedInterfaces; -}`} - - -### State Variables - - - -## Functions - -### getStorage - -Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC165Storage storage s);`} - - -**Returns:** - - - ---- -### registerInterface - -Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` - - -{`function registerInterface(bytes4 _interfaceId) ;`} - - -**Parameters:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {ERC165Mod, IERC165Mod} from "@compose/modules/ERC165Mod.sol"; -import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; - -contract MyERC721Facet { - struct MyFacetStorage { - // ... other storage variables - } - - function initialize(MyFacetStorage storage self, address _diamondAddress) external { - // Register ERC721 interface support - ERC165Mod.registerInterface(type(IERC721).interfaceId); - // ... other initialization logic - } -}`} - - -## Best Practices - - -- Call `registerInterface` during facet initialization to declare supported ERC-165 interfaces. -- Ensure the diamond proxy implementation correctly forwards calls to the ERC165 facet. - - -## Integration Notes - - -The ERC165Mod utilizes a dedicated storage slot for its `ERC165Mod.Storage` struct. The `getStorage` function uses inline assembly to ensure it always binds to the correct storage position. Facets should call `ERC165Mod.registerInterface` during their initialization to declare the interfaces they implement. This registration populates the `supportedInterfaces` mapping within the ERC165 storage. - - -
- -
- - diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json deleted file mode 100644 index 2396f18a..00000000 --- a/website/docs/library/interfaceDetection/ERC165/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-165", - "position": 99, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/interfaceDetection/ERC165/index" - } -} diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx deleted file mode 100644 index 42199b63..00000000 --- a/website/docs/library/interfaceDetection/ERC165/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ERC-165" -description: "ERC-165 components for Compose diamonds." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-165 components for Compose diamonds. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json deleted file mode 100644 index a184d836..00000000 --- a/website/docs/library/interfaceDetection/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Interface Detection", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/interfaceDetection/index" - } -} diff --git a/website/docs/library/interfaceDetection/index.mdx b/website/docs/library/interfaceDetection/index.mdx deleted file mode 100644 index 17feecdd..00000000 --- a/website/docs/library/interfaceDetection/index.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "Interface Detection" -description: "ERC-165 interface detection support." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-165 interface detection support. - - - - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx deleted file mode 100644 index dd343485..00000000 --- a/website/docs/library/token/ERC1155/ERC1155Facet.mdx +++ /dev/null @@ -1,646 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Facet" -description: "Manages ERC-1155 fungible and non-fungible tokens." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 fungible and non-fungible tokens. - - - -- Supports both fungible and non-fungible token types under the ERC-1155 standard. -- Implements batched operations (`balanceOfBatch`, `safeBatchTransferFrom`) for improved efficiency. -- Provides URI resolution for token metadata, allowing for both base URIs and token-specific URIs. - - -## Overview - -The ERC1155Facet provides a standard interface for managing multi-token standards within a Compose diamond. It supports both fungible and non-fungible tokens, enabling batched operations for transfers and balance checks, enhancing efficiency for complex interactions. - ---- - -## Storage - -### ERC1155Storage - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### uri - -Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. - - -{`function uri(uint256 _id) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the amount of tokens of token type `id` owned by `account`. - - -{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOfBatch - -Batched version of balanceOf. - - -{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) - external - view - returns (uint256[] memory balances);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setApprovalForAll - -Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### isApprovedForAll - -Returns true if `operator` is approved to transfer `account`'s tokens. - - -{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### safeTransferFrom - -Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Batched version of safeTransferFrom. Emits a TransferBatch event. - - -{`function safeBatchTransferFrom( - address _from, - address _to, - uint256[] calldata _ids, - uint256[] calldata _values, - bytes calldata _data -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. -
- -
- Signature: - -{`event TransferSingle( - address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. -
- -
- Signature: - -{`event TransferBatch( - address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. -
- -
- Signature: - -{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `id` changes to `value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Error indicating insufficient balance for a transfer. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Error indicating the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Error indicating the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Error indicating missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
- -
- Error indicating the approver address is invalid. -
- -
- Signature: - -error ERC1155InvalidApprover(address _approver); - -
-
- -
- Error indicating the operator address is invalid. -
- -
- Signature: - -error ERC1155InvalidOperator(address _operator); - -
-
- -
- Error indicating array length mismatch in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Facet} from "@compose/contracts/src/facets/ERC1155/IERC1155Facet.sol"; - -contract MyERC1155Consumer { - IERC1155Facet public erc1155Facet; - - constructor(address _diamondAddress) { - erc1155Facet = IERC1155Facet(_diamondAddress); - } - - function consumeERC1155() external { - address owner = msg.sender; - uint256 tokenId = 1; - uint256 amount = 10; - - // Example: Check balance - uint256 balance = erc1155Facet.balanceOf(owner, tokenId); - - // Example: Transfer tokens (assuming caller has approved operator or is owner) - // Note: This requires proper approval setup or calling from an authorized address. - // erc1155Facet.safeTransferFrom(owner, address(this), tokenId, amount, ""); - } -}`} - - -## Best Practices - - -- Initialize the ERC1155 facet with a unique storage slot using the diamond proxy's initializer functions. -- Implement explicit access control mechanisms for functions like `safeTransferFrom` and `safeBatchTransferFrom` if necessary, beyond the standard ERC-1155 approval patterns. -- Utilize `balanceOfBatch` and `safeBatchTransferFrom` for gas efficiency when performing operations on multiple token IDs or accounts. - - -## Security Considerations - - -Ensure that the `operator` address has been granted approval via `setApprovalForAll` before calling transfer functions. Validate `from`, `to`, and `id` parameters to prevent unintended transfers. Be aware of potential reentrancy if external contracts are called within custom logic that interacts with this facet. - - -
- -
- - diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx deleted file mode 100644 index 8f7bc3a4..00000000 --- a/website/docs/library/token/ERC1155/ERC1155Mod.mdx +++ /dev/null @@ -1,601 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC1155Mod" -description: "Manages ERC-1155 token transfers, minting, and burning." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC-1155 token transfers, minting, and burning. - - - -- Supports minting and burning of single and batched ERC-1155 token types. -- Implements safe transfer logic with receiver validation as per EIP-1155. -- Provides functionality to set base URIs and token-specific URIs for metadata. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC1155Mod provides core ERC-1155 token functionality, enabling minting, burning, and safe transfers of single and batched token types. It adheres to EIP-1155 standards, ensuring interoperability and proper handling of token receivers. This module is essential for any diamond that manages fungible or semi-fungible assets. - ---- - -## Storage - -### ERC1155Storage - -ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 - - -{`struct ERC1155Storage { - mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; - mapping(address account => mapping(address operator => bool)) isApprovedForAll; - string uri; - string baseURI; - mapping(uint256 tokenId => string) tokenURIs; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. - - -{`function burn(address _from, uint256 _id, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burnBatch - -Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. - - -{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getStorage() pure returns (ERC1155Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. - - -{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### mintBatch - -Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. - - -{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} - - -**Parameters:** - - - ---- -### safeBatchTransferFrom - -Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeBatchTransferFrom( -address _from, -address _to, -uint256[] memory _ids, -uint256[] memory _values, -address _operator -) ;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. - - -{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} - - -**Parameters:** - - - ---- -### setBaseURI - -Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. - - -{`function setBaseURI(string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### setTokenURI - -Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. - - -{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when multiple token types are transferred. -
- -
- Signature: - -{`event TransferBatch( -address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a single token type is transferred. -
- -
- Signature: - -{`event TransferSingle( -address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value -);`} - -
- -
- Parameters: - -
-
- -
- Emitted when the URI for token type `_id` changes to `_value`. -
- -
- Signature: - -{`event URI(string _value, uint256 indexed _id);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. -
- -
- Signature: - -error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); - -
-
- -
- Thrown when array lengths don't match in batch operations. -
- -
- Signature: - -error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC1155InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC1155InvalidSender(address _sender); - -
-
- -
- Thrown when missing approval for an operator. -
- -
- Signature: - -error ERC1155MissingApprovalForAll(address _operator, address _owner); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC1155Mod} from "../interfaces/IERC1155Mod.sol"; - -contract MyERC1155Facet { - IERC1155Mod internal constant ERC1155 = IERC1155Mod(address(this)); // Replace with actual diamond proxy address - - function mintSomeTokens(address _to, uint256 _id, uint256 _amount) external { - ERC1155.mint(_to, _id, _amount); - } - - function transferMyTokens(address _from, address _to, uint256 _id, uint256 _amount) external { - ERC1155.safeTransferFrom(_from, _to, _id, _amount, ""); - } - - function burnMyTokens(uint256 _id, uint256 _amount) external { - ERC1155.burn(_id, _amount); - } -}`} - - -## Best Practices - - -- Always validate receiver addresses when minting or transferring to contracts using `ERC1155Receiver` interface checks. -- Use `safeTransferFrom` and `safeBatchTransferFrom` for all transfers to ensure receiver contract compatibility. -- Handle `ERC1155InsufficientBalance`, `ERC1155InvalidArrayLength`, `ERC1155InvalidReceiver`, `ERC1155InvalidSender`, and `ERC1155MissingApprovalForAll` errors appropriately. - - -## Integration Notes - - -The ERC1155Mod interacts with a predefined storage slot in the diamond for its state, including balances, approvals, and URI mappings. Facets using this module should ensure that no other facets occupy this specific storage slot to avoid conflicts. The `getStorage` function provides direct access to this storage struct, allowing other facets to read and potentially modify state if designed to do so, adhering to the diamond storage pattern. - - -
- -
- - diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json deleted file mode 100644 index cdb57d9a..00000000 --- a/website/docs/library/token/ERC1155/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-1155", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC1155/index" - } -} diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx deleted file mode 100644 index 4813e2ee..00000000 --- a/website/docs/library/token/ERC1155/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ERC-1155" -description: "ERC-1155 multi-token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-1155 multi-token implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx deleted file mode 100644 index 06b4b2b4..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx +++ /dev/null @@ -1,224 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BurnFacet" -description: "Burn ERC-20 tokens from caller or delegated accounts." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC-20 tokens from caller or delegated accounts. - - - -- Enables reduction of total token supply. -- Supports burning from the caller's balance. -- Supports burning from another account using allowances. - - -## Overview - -The ERC20BurnFacet provides functionality to reduce the total supply of an ERC-20 token. It allows token holders to burn their own tokens or burn tokens on behalf of another account if they have sufficient allowance. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. - - -{`function burn(uint256 _value) external;`} - - -**Parameters:** - - - ---- -### burnFrom - -Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. - - -{`function burnFrom(address _account, uint256 _value) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {DiamondLoupeFacet} from "@compose/diamond-loupe/DiamondLoupeFacet.sol"; -import {ERC20BurnFacet} from "@compose/erc20/facets/ERC20BurnFacet.sol"; - -contract MyDiamond is DiamondLoupeFacet { - // ... other facets - - function burn(uint256 _amount) external { - ERC20BurnFacet(address(this)).burn(_amount); - } - - function burnFrom(address _from, uint256 _amount) external { - ERC20BurnFacet(address(this)).burnFrom(_from, _amount); - } - - // ... other diamond functions -}`} - - -## Best Practices - - -- Ensure the ERC20BurnFacet is correctly added to the diamond proxy during deployment. -- Use `burn` to reduce your own token balance and `burnFrom` to reduce another account's balance when you have been granted allowance. - - -## Security Considerations - - -The `burnFrom` function requires that the caller has been granted sufficient allowance by the `_from` address. Ensure appropriate allowance management mechanisms are in place to prevent unintended token burning. The `Transfer` event is emitted to the zero address when tokens are burned, as per ERC-20 standards. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx deleted file mode 100644 index 5a89eb5c..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx +++ /dev/null @@ -1,544 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Facet" -description: "Implements the ERC-20 token standard for Compose diamonds." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements the ERC-20 token standard for Compose diamonds. - - - -- Full ERC-20 standard compliance. -- Operates via the diamond proxy, enabling upgradeability and composability. -- Uses a dedicated storage slot for ERC-20 state, ensuring isolation. - - -## Overview - -The ERC20Facet provides a standard interface for fungible tokens within a Compose diamond. It handles core ERC-20 operations like transfers, approvals, and balance inquiries, enabling tokens to be seamlessly integrated and managed by the diamond proxy. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - -### State Variables - - - -## Functions - -### name - -Returns the name of the token. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### decimals - -Returns the number of decimals used for token precision. - - -{`function decimals() external view returns (uint8);`} - - -**Returns:** - - - ---- -### totalSupply - -Returns the total supply of tokens. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the balance of a specific account. - - -{`function balanceOf(address _account) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. - - -{`function allowance(address _owner, address _spender) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. - - -{`function approve(address _spender, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers tokens to another address. Emits a Transfer event. - - -{`function transfer(address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. - - -{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when an account has insufficient balance for a transfer or burn. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when a spender tries to use more than the approved allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Facet} from "@compose-protocol/diamond/contracts/facets/ERC20/IERC20Facet.sol"; - -contract ERC20Consumer { - IERC20Facet private erc20Facet; - - function setERC20Facet(address _diamondProxy) public { - erc20Facet = IERC20Facet(_diamondProxy); - } - - function getTokenName() public view returns (string memory) { - return erc20Facet.name(); - } - - function transferTokens(address _to, uint256 _amount) public { - erc20Facet.transfer(_to, _amount); - } - - function getBalance(address _account) public view returns (uint256) { - return erc20Facet.balanceOf(_account); - } -}`} - - -## Best Practices - - -- Initialize the ERC20Facet with the correct diamond storage slot for ERC-20 state. -- Ensure appropriate access control is configured in the diamond loupe for sensitive functions like `approve` and `transfer` if needed. -- Store the ERC20Facet's address in consumer contracts to interact with token functionalities. - - -## Security Considerations - - -Standard ERC-20 security considerations apply, including potential reentrancy risks on `transfer` and `transferFrom` if not handled carefully by the caller. Ensure sufficient allowances are set before calling `transferFrom`. Input validation is handled by custom errors: `ERC20InsufficientBalance`, `ERC20InvalidSender`, `ERC20InvalidReceiver`, `ERC20InsufficientAllowance`, `ERC20InvalidSpender`. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx deleted file mode 100644 index 66a9774c..00000000 --- a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx +++ /dev/null @@ -1,427 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20Mod" -description: "ERC-20 token logic with mint, burn, and transfer capabilities." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-20 token logic with mint, burn, and transfer capabilities. - - - -- Implements core ERC-20 functions: transfer, transferFrom, approve, mint, burn. -- Manages token balances and allowances internally. -- Provides a `getStorage` function for direct access to internal storage, useful for read-only operations or specific integrations. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the core logic for ERC-20 token functionality within a Compose diamond. It manages token balances, allowances, and total supply, enabling standard token operations like minting, burning, and transfers. Implementing this module ensures compatibility with the ERC-20 standard while leveraging the diamond's composable architecture. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; - string symbol; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. - - -{`function approve(address _spender, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns tokens from a specified address. Decreases both total supply and the sender's balance. - - -{`function burn(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. - - -{`function getStorage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints new tokens to a specified address. Increases both total supply and the recipient's balance. - - -{`function mint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. - - -{`function transfer(address _to, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. - - -{`function transferFrom(address _from, address _to, uint256 _value) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a spender tries to spend more than their allowance. -
- -
- Signature: - -error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); - -
-
- -
- Thrown when a sender attempts to transfer or burn more tokens than their balance. -
- -
- Signature: - -error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Mod } from "../facets/ERC20Mod.sol"; -import { IDiamondProxy } from "../diamond/IDiamondProxy.sol"; - -contract ERC20Consumer { - IDiamondProxy public immutable diamondProxy; - - constructor(address _diamondProxyAddress) { - diamondProxy = IDiamondProxy(_diamondProxyAddress); - } - - function consumeERC20() external { - IERC20Mod erc20Facet = IERC20Mod(address(diamondProxy)); - - // Example: Transfer tokens - erc20Facet.transfer(msg.sender, 100); - - // Example: Approve a spender - erc20Facet.approve(address(this), 50); - - // Example: Mint tokens (requires appropriate permissions) - // erc20Facet.mint(msg.sender, 1000); - - // Example: Burn tokens - // erc20Facet.burn(50); - } -}`} - - -## Best Practices - - -- Ensure the ERC20Mod facet is correctly initialized and added to the diamond proxy. -- Implement appropriate access control for mint and burn functions if they are intended for specific roles. -- Handle potential errors such as insufficient balance, allowance, or invalid addresses. - - -## Integration Notes - - -The ERC20Mod facet interacts with a dedicated storage slot for ERC-20 state, including balances, allowances, and total supply. Facets that interact with ERC-20 tokens should call functions on the ERC20Mod facet via the diamond proxy. The `getStorage` function provides a mechanism to retrieve a pointer to the ERC-20 storage struct, enabling direct access to its state, but this should be done with caution to maintain data integrity and consider diamond upgrade scenarios. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json deleted file mode 100644 index bd8d3da5..00000000 --- a/website/docs/library/token/ERC20/ERC20/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC20/ERC20/index" - } -} diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx deleted file mode 100644 index 272ec9e0..00000000 --- a/website/docs/library/token/ERC20/ERC20/index.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: "ERC-20" -description: "ERC-20 fungible token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-20 fungible token implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx deleted file mode 100644 index 8aebef2e..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx +++ /dev/null @@ -1,388 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableFacet" -description: "Facilitates cross-chain token transfers for ERC20 tokens." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Facilitates cross-chain token transfers for ERC20 tokens. - - - -- Enables cross-chain minting of ERC20 tokens. -- Supports cross-chain burning of ERC20 tokens. -- Restricts cross-chain operations to addresses with the `trusted-bridge` role. - - -## Overview - -The ERC20BridgeableFacet enables secure cross-chain minting and burning operations for ERC20 tokens within a Compose diamond. It integrates with the diamond's access control to ensure only trusted bridge operators can initiate these operations. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - ---- -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - -### State Variables - - - -## Functions - -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) external;`} - - -**Parameters:** - - - ---- -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) external view;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20BridgeableFacet} from "@compose-protocol/diamond-contracts/facets/ERC20Bridgeable/IERC20BridgeableFacet.sol"; -import {DiamondProxy} from "@compose-protocol/diamond-contracts/diamond/DiamondProxy.sol"; - -contract ExampleUser { - address internal diamondProxyAddress; - - constructor(address _diamondProxyAddress) { - diamondProxyAddress = _diamondProxyAddress; - } - - function mintCrosschain(address _token, uint256 _amount, address _to) external { - bytes4 selector = IERC20BridgeableFacet.crosschainMint.selector; - (bool success, bytes memory data) = diamondProxyAddress.call(abi.encodeWithSelector(selector, _token, _amount, _to)); - require(success, "Crosschain mint failed"); - } - - function burnCrosschain(address _token, uint256 _amount, address _from) external { - bytes4 selector = IERC20BridgeableFacet.crosschainBurn.selector; - (bool success, bytes memory data) = diamondProxyAddress.call(abi.encodeWithSelector(selector, _token, _amount, _from)); - require(success, "Crosschain burn failed"); - } -}`} - - -## Best Practices - - -- Ensure the `trusted-bridge` role is granted only to secure, audited bridge contracts or multisigs. -- Use `checkTokenBridge` internally or within other facets to verify bridge trust before executing cross-chain operations. -- Retrieve storage structs using `getERC20Storage` and `getAccessControlStorage` for accurate state access. - - -## Security Considerations - - -Cross-chain operations are sensitive. Ensure that the `trusted-bridge` role is strictly managed. Input validation for token addresses, amounts, and recipient/sender addresses is critical to prevent token loss or unintended state changes. Reentrancy is not directly applicable to the cross-chain functions themselves, but downstream effects of minting/burning should be considered. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx deleted file mode 100644 index 6dbb9cf6..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx +++ /dev/null @@ -1,436 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20BridgeableMod" -description: "Manage cross-chain ERC20 token bridging." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage cross-chain ERC20 token bridging. - - - -- Restricted access for cross-chain operations via `trusted-bridge` role. -- Facilitates both burning and minting of ERC20 tokens for inter-chain transfers. -- Integrates with the diamond storage pattern for access control and ERC20 state management. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC20Bridgeable module provides functionality for cross-chain ERC20 token transfers. It enforces access control for trusted bridge operators and facilitates the burning and minting of tokens across different chains. This module is essential for interoperable token ecosystems built on Compose diamonds. - ---- - -## Storage - -### AccessControlStorage - - -{`struct AccessControlStorage { - mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; -}`} - - ---- -### ERC20Storage - -ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; -}`} - - -### State Variables - - - -## Functions - -### checkTokenBridge - -Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. - - -{`function checkTokenBridge(address _caller) view;`} - - -**Parameters:** - - - ---- -### crosschainBurn - -Cross-chain burn — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainBurn(address _from, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### crosschainMint - -Cross-chain mint — callable only by an address having the `trusted-bridge` role. - - -{`function crosschainMint(address _account, uint256 _value) ;`} - - -**Parameters:** - - - ---- -### getAccessControlStorage - -helper to return AccessControlStorage at its diamond slot - - -{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} - - ---- -### getERC20Storage - -Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - -**Returns:** - - - -## Events - - - -
- Emitted when a crosschain transfer burns tokens. -
- -
- Signature: - -{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are minted via a cross-chain bridge. -
- -
- Signature: - -{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} - -
- -
- Parameters: - -
-
- -
- Emitted when tokens are transferred between two addresses. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the account does not have a specific role. -
- -
- Signature: - -error AccessControlUnauthorizedAccount(address _account, bytes32 _role); - -
-
- - -
- Signature: - -error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); - -
-
- -
- Revert when caller is not a trusted bridge. -
- -
- Signature: - -error ERC20InvalidBridgeAccount(address _caller); - -
-
- -
- Revert when caller address is invalid. -
- -
- Signature: - -error ERC20InvalidCallerAddress(address _caller); - -
-
- -
- /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . -
- -
- Signature: - -error ERC20InvalidReciever(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSender(address _sender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Bridgeable } from "@compose/modules/ERC20Bridgeable.sol"; - -contract MyFacet { - address immutable DIAMOND_FACET_CUTTER; // Assume this is set - address immutable DIAMOND_PROXY; // Assume this is set - - constructor(address _diamondProxy, address _diamondFacetCutter) { - DIAMOND_PROXY = _diamondProxy; - DIAMOND_FACET_CUTTER = _diamondFacetCutter; - } - - /** - * @notice Burns tokens for cross-chain transfer. - * @param _token Address of the ERC20 token to burn. - * @param _amount Amount of tokens to burn. - * @param _to Address on the destination chain. - */ - function burnForBridge(address _token, uint256 _amount, address _to) external { - // Assuming IERC20Bridgeable is deployed and accessible via the diamond proxy - IERC20Bridgeable(DIAMOND_PROXY).crosschainBurn(_token, _amount, _to); - } - - /** - * @notice Mints tokens for cross-chain transfer. - * @param _token Address of the ERC20 token to mint. - * @param _to Address on the destination chain. - * @param _amount Amount of tokens to mint. - */ - function mintFromBridge(address _token, address _to, uint256 _amount) external { - // Assuming IERC20Bridgeable is deployed and accessible via the diamond proxy - IERC20Bridgeable(DIAMOND_PROXY).crosschainMint(_token, _to, _amount); - } -}`} - - -## Best Practices - - -- Ensure only addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint`. -- Validate recipient addresses on the destination chain to prevent `ERC20InvalidReciever` errors. -- Handle `ERC20InsufficientBalance` errors gracefully in your application logic when burning tokens. - - -## Integration Notes - - -This module relies on the `AccessControl` and `ERC20` modules for its operations. The `crosschainBurn` and `crosschainMint` functions require the caller to be authorized by the `trusted-bridge` role within the `AccessControl` module. The `checkTokenBridge` internal function verifies this authorization. The module uses predefined diamond storage slots for both `AccessControlStorage` and `ERC20Storage`, accessed via inline assembly in helper functions like `getAccessControlStorage` and `getERC20Storage`. Ensure these storage slots are correctly configured and accessible by the ERC20Bridgeable facet. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json deleted file mode 100644 index 03768f44..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20 Bridgeable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC20/ERC20Bridgeable/index" - } -} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx deleted file mode 100644 index d66f1fd9..00000000 --- a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ERC-20 Bridgeable" -description: "ERC-20 Bridgeable extension for ERC-20 tokens." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-20 Bridgeable extension for ERC-20 tokens. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx deleted file mode 100644 index efb5c61a..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx +++ /dev/null @@ -1,329 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitFacet" -description: "Manage ERC-20 token allowances via EIP-2612 permit signatures." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC-20 token allowances via EIP-2612 permit signatures. - - - -- Implements EIP-2612 `permit` function for ERC-20 token approvals. -- Uses signed messages to authorize allowances, reducing on-chain transactions for users. -- Provides `nonces` and `DOMAIN_SEPARATOR` for signature verification. - - -## Overview - -The ERC20PermitFacet enables EIP-2612 compliant meta-transactions for ERC-20 token approvals. It allows users to grant allowances to spenders without directly interacting with the ERC-20 token contract, leveraging signed messages for off-chain authorization. This enhances user experience by reducing gas costs and simplifying the approval process. - ---- - -## Storage - -### ERC20Storage - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - ---- -### ERC20PermitStorage - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - -### State Variables - - - -## Functions - -### nonces - -Returns the current nonce for an owner. This value changes each time a permit is used. - - -{`function nonces(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} - - -**Returns:** - - - ---- -### permit - -Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. - - -{`function permit( - address _owner, - address _spender, - uint256 _value, - uint256 _deadline, - uint8 _v, - bytes32 _r, - bytes32 _s -) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( - address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
- -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20Permit, ERC20PermitFacet} from "@compose-protocol/diamond/contracts/facets/ERC20Permit/ERC20PermitFacet.sol"; -import {IDiamondCut, DiamondLoupeFacet} from "@compose-protocol/diamond/contracts/facets/DiamondLoupe/DiamondLoupeFacet.sol"; - -contract MyDiamond is IERC20Permit { - // ... other facets - - function addERC20PermitFacet(address _diamondCutFacet) external { - ERC20PermitFacet erc20PermitFacet = new ERC20PermitFacet(); - bytes32[] memory selectors = new bytes32[](5); - selectors[0] = ERC20PermitFacet.getERC20Storage.selector; - selectors[1] = ERC20PermitFacet.getStorage.selector; - selectors[2] = ERC20PermitFacet.nonces.selector; - selectors[3] = ERC20PermitFacet.DOMAIN_SEPARATOR.selector; - selectors[4] = ERC20PermitFacet.permit.selector; - - IDiamondCut(_diamondCutFacet).diamondCut(new IDiamondCut.FacetCut[](0), erc20PermitFacet, selectors); - } - - // Implementation of IERC20Permit to route to the facet - function nonces(address owner) public view override returns (uint256) { - bytes4 selector = ERC20PermitFacet.nonces.selector; - (bool success, bytes memory result) = address(this).staticcall(abi.encodeWithSelector(selector, owner)); - require(success, "Nonces call failed"); - return abi.decode(result, (uint256)); - } - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public override { - bytes4 selector = ERC20PermitFacet.permit.selector; - (bool success, ) = address(this).call(abi.encodeWithSelector(selector, owner, spender, value, deadline, v, r, s)); - require(success, "Permit call failed"); - } - - // ... other IERC20Permit functions -}`} - - -## Best Practices - - -- Ensure the `ERC20PermitFacet` is added to the diamond during deployment or upgrade. -- Users will call the `permit` function via the diamond proxy, which will then route the call to the appropriate facet. -- Store the `DOMAIN_SEPARATOR` and `nonces` appropriately within the diamond's storage. - - -## Security Considerations - - -Users must verify the `deadline` to prevent stale permits from being used. The `DOMAIN_SEPARATOR` prevents replay attacks across different chains or contracts. Ensure the underlying ERC-20 token contract is correctly integrated and accessible for the allowance updates to take effect. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx deleted file mode 100644 index b9ebd42d..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx +++ /dev/null @@ -1,278 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC20PermitMod" -description: "ERC-2612 Permit and Domain Separator Logic" -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2612 Permit and Domain Separator Logic - - - -- Generates the domain separator required for ERC-2612 signatures. -- Validates owner signatures for token approvals. -- Allows for gasless token approvals by enabling off-chain signing. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -This module provides the core logic for implementing the ERC-2612 Permit functionality. It handles domain separator generation and permit signature validation, enabling gasless token approvals for users. Integrating this module allows your diamond to support off-chain signature approvals for ERC-20 tokens. - ---- - -## Storage - -### ERC20PermitStorage - -storage-location: erc8042:compose.erc20.permit - - -{`struct ERC20PermitStorage { - mapping(address owner => uint256) nonces; -}`} - - ---- -### ERC20Storage - -storage-location: erc8042:compose.erc20 - - -{`struct ERC20Storage { - mapping(address owner => uint256 balance) balanceOf; - uint256 totalSupply; - mapping(address owner => mapping(address spender => uint256 allowance)) allowance; - uint8 decimals; - string name; -}`} - - -### State Variables - - - -## Functions - -### DOMAIN_SEPARATOR - -Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. - - -{`function DOMAIN_SEPARATOR() view returns (bytes32);`} - - -**Returns:** - - - ---- -### getERC20Storage - - -{`function getERC20Storage() pure returns (ERC20Storage storage s);`} - - ---- -### getPermitStorage - - -{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} - - ---- -### permit - -Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. - - -{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval is made for a spender by an owner. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC20InvalidSpender(address _spender); - -
-
- -
- Thrown when a permit signature is invalid or expired. -
- -
- Signature: - -error ERC2612InvalidSignature( -address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s -); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC20PermitMod} from "@compose/modules/ERC20PermitMod.sol"; - -contract MyERC20Facet { - IERC20PermitMod private immutable _erc20PermitMod; - - constructor(address _erc20PermitModAddress) { - _erc20PermitMod = IERC20PermitMod(_erc20PermitModAddress); - } - - function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { - // Delegate permit validation and allowance setting to the module. - // The module requires the diamond's address to generate the correct domain separator. - // The calling facet must emit the Approval event as per ERC-20 and ERC-2612 standards. - _erc20PermitMod.permit(owner, spender, value, deadline, v, r, s, address(this)); - emit Approval(owner, spender, value); - } - - // ... other ERC-20 functions -}`} - - -## Best Practices - - -- Ensure the `permit` function in your facet correctly emits the `Approval` event after calling the module's `permit` function. -- Verify that the `DOMAIN_SEPARATOR` is correctly calculated and utilized by the module; this is crucial for signature validity. -- Implement appropriate access control for functions that might interact with or trigger the permit logic, if necessary, though permit itself is permissionless for the owner. - - -## Integration Notes - - -The `ERC20PermitMod` library requires access to the diamond's address to correctly compute the domain separator. The `permit` function in this module validates the signature and updates the allowance, but it does not emit the `Approval` event. The facet calling the module's `permit` function must emit the `Approval` event to comply with ERC-20 and ERC-2612 standards. Storage for allowances is assumed to be managed by another facet that the `ERC20PermitMod` can interact with or that the calling facet updates based on the module's validation. - - -
- -
- - diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json deleted file mode 100644 index 7932c4df..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20 Permit", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC20/ERC20Permit/index" - } -} diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx deleted file mode 100644 index 7394bf87..00000000 --- a/website/docs/library/token/ERC20/ERC20Permit/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ERC-20 Permit" -description: "ERC-20 Permit extension for ERC-20 tokens." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-20 Permit extension for ERC-20 tokens. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json deleted file mode 100644 index 0e078cb1..00000000 --- a/website/docs/library/token/ERC20/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-20", - "position": 1, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC20/index" - } -} diff --git a/website/docs/library/token/ERC20/index.mdx b/website/docs/library/token/ERC20/index.mdx deleted file mode 100644 index 0bb39d2d..00000000 --- a/website/docs/library/token/ERC20/index.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: "ERC-20" -description: "ERC-20 fungible token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-20 fungible token implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx deleted file mode 100644 index 1804a6c7..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx +++ /dev/null @@ -1,504 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Facet" -description: "Manage ERC-6909 token balances and operator permissions." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manage ERC-6909 token balances and operator permissions. - - - -- Implements ERC-6909 standard for fungible tokens. -- Supports direct token transfers and transfers via approved spenders. -- Enables management of operator relationships for token management. - - -## Overview - -This facet implements the ERC-6909 standard, enabling fungible token transfers and operator management within a Compose diamond. It provides essential functions for checking balances, allowances, and managing operator relationships, integrating seamlessly with the diamond's storage pattern. - ---- - -## Storage - -### ERC6909Storage - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### balanceOf - -Owner balance of an id. - - -{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### allowance - -Spender allowance of an id. - - -{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isOperator - -Checks if a spender is approved by an owner as an operator. - - -{`function isOperator(address _owner, address _spender) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transfer - -Transfers an amount of an id from the caller to a receiver. - - -{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### transferFrom - -Transfers an amount of an id from a sender to a receiver. - - -{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _spender, bool _approved) external returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - -## Events - - - - -
- Signature: - -{`event Transfer( - address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- - -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- - -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Facet} from "@compose/core/src/facets/ERC6909/IERC6909Facet.sol"; - -contract ERC6909Consumer { - // Assume diamond interface is available - IERC6909Facet internal immutable erc6909Facet; - - constructor(address _diamondAddress) { - erc6909Facet = IERC6909Facet(_diamondAddress); - } - - function getBalance(address _owner, uint256 _id) public view returns (uint256) { - return erc6909Facet.balanceOf(_owner, _id); - } - - function transferTokens(address _to, uint256 _id, uint256 _amount) public { - erc6909Facet.transfer(_to, _id, _amount); - } - - function approveSpender(address _spender, uint256 _id, uint256 _amount) public { - erc6909Facet.approve(_spender, _id, _amount); - } -}`} - - -## Best Practices - - -- Initialize the facet correctly within the diamond deployment process. -- Ensure adequate access control is implemented at the diamond level for sensitive functions like `setOperator` if required by your application logic. -- When upgrading, preserve storage layout compatibility as per Compose guidelines. - - -## Security Considerations - - -Functions like `transfer` and `transferFrom` should have appropriate checks for sufficient balance and allowance to prevent underflows and unauthorized spending. The `setOperator` function should be guarded if its usage requires specific permissions. Input validation on `_id`, `_amount`, `_owner`, `_receiver`, `_sender`, and `_spender` is crucial to prevent unexpected behavior and potential exploits. - - -
- -
- - diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx deleted file mode 100644 index 21c4c6b4..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx +++ /dev/null @@ -1,534 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC6909Mod" -description: "Implements ERC-6909 minimal multi-token logic." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Implements ERC-6909 minimal multi-token logic. - - - -- Supports standard ERC-6909 token operations: mint, burn, transfer, approve. -- Manages operator relationships for delegated spending. -- Utilizes the diamond storage pattern for state management via `getStorage`. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC6909Mod provides the core logic and storage structure for implementing the ERC-6909 standard. It enables managing multiple token types with standard transfer, approval, and operator functionalities within a diamond. This module ensures composability by adhering to the diamond storage pattern for its state. - ---- - -## Storage - -### ERC6909Storage - -storage-location: erc8042:compose.erc6909 - - -{`struct ERC6909Storage { - mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; - mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; - mapping(address owner => mapping(address spender => bool)) isOperator; -}`} - - -### State Variables - - - -## Functions - -### approve - -Approves an amount of an id to a spender. - - -{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### burn - -Burns `_amount` of token id `_id` from `_from`. - - -{`function burn(address _from, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. - - -{`function getStorage() pure returns (ERC6909Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints `_amount` of token id `_id` to `_to`. - - -{`function mint(address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - ---- -### setOperator - -Sets or removes a spender as an operator for the caller. - - -{`function setOperator(address _owner, address _spender, bool _approved) ;`} - - -**Parameters:** - - - ---- -### transfer - -Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. - - -{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when an approval occurs. -
- -
- Signature: - -{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} - -
- -
- Parameters: - -
-
- -
- Emitted when an operator is set. -
- -
- Signature: - -{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} - -
- -
- Parameters: - -
-
- -
- Emitted when a transfer occurs. -
- -
- Signature: - -{`event Transfer( -address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount -);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the spender has insufficient allowance. -
- -
- Signature: - -error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the sender has insufficient balance. -
- -
- Signature: - -error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); - -
-
- -
- Thrown when the approver address is invalid. -
- -
- Signature: - -error ERC6909InvalidApprover(address _approver); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC6909InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSender(address _sender); - -
-
- -
- Thrown when the spender address is invalid. -
- -
- Signature: - -error ERC6909InvalidSpender(address _spender); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC6909Mod} from "./IERC6909Mod.sol"; -import {ERC6909Storage} from "./ERC6909Storage.sol"; - -contract ERC6909Facet { - // Assume STORAGE_POSITION is defined and accessible - uint256 private constant STORAGE_POSITION = 1; // Example slot - - function approve(uint256 _id, address _spender, uint256 _amount) external { - IERC6909Mod(address(this)).approve(_id, _spender, _amount); - } - - function transfer(address _from, address _to, uint256 _id, uint256 _amount) external { - IERC6909Mod(address(this)).transfer(_from, _to, _id, _amount); - } - - function mint(address _to, uint256 _id, uint256 _amount) external { - IERC6909Mod(address(this)).mint(_to, _id, _amount); - } - - function burn(address _from, uint256 _id, uint256 _amount) external { - IERC6909Mod(address(this)).burn(_from, _id, _amount); - } - - function setOperator(address _operator, bool _approved) external { - IERC6909Mod(address(this)).setOperator(_operator, _approved); - } - - function getStorage() internal view returns (ERC6909Storage storage s) { - bytes32 position = keccak256(abi.encodePacked(STORAGE_POSITION)); - assembly { - s := sload(position) - } - } -}`} - - -## Best Practices - - -- Ensure the `ERC6909Storage` struct is correctly defined and placed in storage according to the `STORAGE_POSITION`. -- Handle `ERC6909InsufficientAllowance` and `ERC6909InsufficientBalance` errors appropriately in calling facets. -- Be mindful of operator status when performing transfers, as it bypasses allowance checks. - - -## Integration Notes - - -The ERC6909Mod relies on a specific storage slot defined by `STORAGE_POSITION` to hold its `ERC6909Storage` struct. Facets interacting with this module must ensure this slot is correctly allocated and that the `ERC6909Storage` struct is compatible. The `getStorage` function provides an assembly-based method to access this struct, maintaining the diamond's storage pattern. Any changes to the ERC6909Storage struct layout in future versions must be handled with care to maintain backward compatibility, especially regarding trailing fields. - - -
- -
- - diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json deleted file mode 100644 index d4d084dc..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC6909/ERC6909/index" - } -} diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx deleted file mode 100644 index 8b036c19..00000000 --- a/website/docs/library/token/ERC6909/ERC6909/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ERC-6909" -description: "ERC-6909 minimal multi-token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-6909 minimal multi-token implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json deleted file mode 100644 index 42f1101f..00000000 --- a/website/docs/library/token/ERC6909/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-6909", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC6909/index" - } -} diff --git a/website/docs/library/token/ERC6909/index.mdx b/website/docs/library/token/ERC6909/index.mdx deleted file mode 100644 index dab5e87f..00000000 --- a/website/docs/library/token/ERC6909/index.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "ERC-6909" -description: "ERC-6909 minimal multi-token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-6909 minimal multi-token implementations. - - - - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx deleted file mode 100644 index 710f784e..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx +++ /dev/null @@ -1,186 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721BurnFacet" -description: "Burn ERC721 tokens within a Compose diamond." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721BurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Burn ERC721 tokens within a Compose diamond. - - - -- Burns ERC721 tokens, permanently removing them. -- Emits standard `Transfer` and `ApprovalForAll` events upon successful burning. -- Integrates seamlessly with the Compose diamond proxy pattern. - - -## Overview - -The ERC721BurnFacet provides the functionality to burn (destroy) ERC721 tokens. It integrates with the diamond proxy to manage token destruction, emitting standard ERC721 events. This facet ensures tokens are properly removed from tracking and ownership. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721BurnFacet} from "@compose/core/src/facets/ERC721/IERC721BurnFacet.sol"; - -contract ERC721BurnConsumer { - address immutable diamondAddress; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function burnToken(uint256 _tokenId) external { - bytes4 selector = IERC721BurnFacet.burn.selector; - // Call the burn function via the diamond proxy - (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _tokenId)); - require(success, \"ERC721BurnFacet: burn failed\"); - } -}`} - - -## Best Practices - - -- Ensure the ERC721BurnFacet is correctly initialized and added to the diamond proxy. -- Verify that the caller has the necessary approvals or ownership to burn the specified token before invoking the burn function. - - -## Security Considerations - - -Access control for burning is typically handled by the ERC721 standard itself (owner or approved address). Ensure that the caller possesses the correct permissions before attempting to burn a token. The `burn` function checks for token existence and sufficient approval, preventing invalid burn operations. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx deleted file mode 100644 index f159ac07..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx +++ /dev/null @@ -1,611 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Facet" -description: "Manages ERC721 token ownership, transfers, and approvals." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Facet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages ERC721 token ownership, transfers, and approvals. - - - -- Implements core ERC721 token management functions. -- Supports token transfers with and without `data` payload for safe transfers. -- Provides mechanisms for approving individual token transfers and operator approvals. - - -## Overview - -The ERC721Facet provides standard ERC721 functionality for token collections within a Compose diamond. It handles token ownership, metadata URIs, balance queries, and approval workflows, enabling composability with other facets. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### name - -Returns the token collection name. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the token collection symbol. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by a given address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns true if an operator is approved to manage all of an owner's assets. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer the given token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes permission for an operator to manage all caller's assets. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking if the receiver can handle ERC-721 tokens. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Facet} from "@compose-protocol/core/contracts/facets/ERC721/IERC721Facet.sol"; - -contract ERC721Consumer { - IERC721Facet public immutable erc721Facet; - - constructor(address _erc721FacetAddress) { - erc721Facet = IERC721Facet(_erc721FacetAddress); - } - - function getTokenName() external view returns (string memory) { - return erc721Facet.name(); - } - - function getTokenSymbol() external view returns (string memory) { - return erc721Facet.symbol(); - } - - function getOwner(uint256 tokenId) external view returns (address) { - return erc721Facet.ownerOf(tokenId); - } - - function transferToken(address to, uint256 tokenId) external { - // Assumes caller is owner or approved - erc721Facet.transferFrom(msg.sender, to, tokenId); - } -}`} - - -## Best Practices - - -- Ensure the caller has the necessary approval or ownership before invoking transfer functions. -- Utilize `safeTransferFrom` when transferring to contracts to ensure receiver compatibility. -- Store the ERC721Facet's address in a read-only or immutable variable for consistent access. - - -## Security Considerations - - -Ensure proper access control checks are performed by the caller before invoking transfer functions. Use `safeTransferFrom` to prevent reentrancy issues when transferring to unknown contract addresses. Validate `to` and `from` addresses in transfer functions to prevent unexpected behavior. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx deleted file mode 100644 index 8547661b..00000000 --- a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx +++ /dev/null @@ -1,354 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721Mod" -description: "Internal logic for ERC-721 token management." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Mod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Internal logic for ERC-721 token management. - - - -- Manages core ERC-721 state transitions (mint, burn, transfer) internally. -- Utilizes Compose's diamond storage pattern for predictable state management. -- Provides explicit error types for common ERC-721 failures. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -ERC721Mod provides the core internal logic for managing ERC-721 tokens within a Compose diamond. It abstracts the complexities of token minting, burning, and transfers, allowing custom facets to integrate ERC-721 functionality safely and efficiently. By leveraging Compose's storage pattern, it ensures state is managed predictably across diamond upgrades. - ---- - -## Storage - -### ERC721Storage - - -{`struct ERC721Storage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. - - -{`function burn(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (ERC721Storage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### setMetadata - - -{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks sufficient approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid (e.g., zero address). -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721Mod } from "@compose/modules/erc721/ERC721Mod.sol"; - -contract MyERC721Facet { - IERC721Mod public immutable erc721Mod; - - constructor(address _erc721ModAddress) { - erc721Mod = IERC721Mod(_erc721ModAddress); - } - - function mintToken(address _to, uint256 _tokenId) external { - erc721Mod.mint(_to, _tokenId); - } - - function burnToken(uint256 _tokenId) external { - erc721Mod.burn(_tokenId); - } -}`} - - -## Best Practices - - -- Ensure the ERC721Mod contract is correctly initialized and accessible by facets via its address. -- Always validate input parameters for token IDs and addresses before calling module functions. -- Handle potential `ERC721...` errors gracefully in facet logic. - - -## Integration Notes - - -ERC721Mod relies on a predefined storage slot for its `ERC721Storage` struct. Facets interacting with this module should be aware that operations like `mint`, `burn`, and `transferFrom` directly modify this shared storage. The `getStorage` function allows facets to read the current state of the ERC-721 storage. Any facet implementing ERC-721 functionality must respect the invariants established by this module, such as token uniqueness and ownership rules. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json deleted file mode 100644 index 219beb4e..00000000 --- a/website/docs/library/token/ERC721/ERC721/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC721/ERC721/index" - } -} diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx deleted file mode 100644 index 29d42e19..00000000 --- a/website/docs/library/token/ERC721/ERC721/index.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: "ERC-721" -description: "ERC-721 non-fungible token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-721 non-fungible token implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx deleted file mode 100644 index 5b26e010..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx +++ /dev/null @@ -1,206 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableBurnFacet" -description: "Enables burning of ERC721 tokens and maintains enumeration." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enables burning of ERC721 tokens and maintains enumeration. - - - -- Supports the burning of ERC721 tokens, removing them from circulation. -- Maintains the integrity of token enumeration after a burn operation, ensuring `totalSupply` and token indices remain accurate. - - -## Overview - -The ERC721EnumerableBurnFacet provides functionality to burn ERC721 tokens. It integrates with the diamond proxy pattern to manage token destruction while ensuring that the enumeration of remaining tokens is correctly updated. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) a token, removing it from enumeration tracking. - - -{`function burn(uint256 _tokenId) external;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- -
- Thrown when the caller lacks approval to operate on the token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableBurnFacet} from "@compose/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; - -contract MyDiamond { - // Assume diamond deployment and initialization have occurred. - // The ERC721EnumerableBurnFacet has been added and selectors are registered. - - // Example of calling the burn function - function burnToken(address _to, uint256 _tokenId) external { - // Get the facet instance - IERC721EnumerableBurnFacet burnFacet = IERC721EnumerableBurnFacet(address(this)); - - // Call the burn function - // Note: Access control for who can burn should be implemented externally or via a separate facet. - burnFacet.burn(_to, _tokenId); - } - - // Example of getting storage (for diagnostic purposes or advanced logic) - function getBurnFacetStorage() external view returns (IERC721EnumerableBurnFacet.ERC721EnumerableBurnStorage memory) { - IERC721EnumerableBurnFacet burnFacet = IERC721EnumerableBurnFacet(address(this)); - return burnFacet.getStorage(); - } -}`} - - -## Best Practices - - -- Ensure proper access control is implemented in a separate facet or contract before allowing calls to the `burn` function to prevent unauthorized token destruction. -- Integrate this facet into your diamond's upgrade process, ensuring the `Transfer` event is handled correctly by your diamond's event aggregator if applicable. - - -## Security Considerations - - -The `burn` function should be protected by robust access control mechanisms to prevent unauthorized token destruction. Ensure that the `_to` address passed to `burn` is the current owner of the token being burned, and that the caller has the necessary approval or ownership rights. The `ERC721NonexistentToken` and `ERC721InsufficientApproval` errors provide basic validation. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx deleted file mode 100644 index 25a07d40..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx +++ /dev/null @@ -1,680 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableFacet" -description: "Enumerable ERC-721 token functionality" -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enumerable ERC-721 token functionality - - - -- Provides `totalSupply`, `balanceOf`, and `ownerOf` for standard ERC-721 queries. -- Enables efficient token enumeration with `tokenOfOwnerByIndex`. -- Supports standard ERC-721 transfer and approval functions. - - -## Overview - -This facet extends ERC-721 functionality by adding enumeration capabilities. It allows querying the total supply, balances, owner of specific tokens, and iterating through tokens owned by an address by index. This provides essential features for managing and interacting with collections of unique digital assets. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### name - -Returns the name of the token collection. - - -{`function name() external view returns (string memory);`} - - -**Returns:** - - - ---- -### symbol - -Returns the symbol of the token collection. - - -{`function symbol() external view returns (string memory);`} - - -**Returns:** - - - ---- -### tokenURI - -Provide the metadata URI for a given token ID. - - -{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### totalSupply - -Returns the total number of tokens in existence. - - -{`function totalSupply() external view returns (uint256);`} - - -**Returns:** - - - ---- -### balanceOf - -Returns the number of tokens owned by an address. - - -{`function balanceOf(address _owner) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### ownerOf - -Returns the owner of a given token ID. - - -{`function ownerOf(uint256 _tokenId) public view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### tokenOfOwnerByIndex - -Returns a token ID owned by a given address at a specific index. - - -{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### getApproved - -Returns the approved address for a given token ID. - - -{`function getApproved(uint256 _tokenId) external view returns (address);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### isApprovedForAll - -Returns whether an operator is approved for all tokens of an owner. - - -{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### approve - -Approves another address to transfer a specific token ID. - - -{`function approve(address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### setApprovalForAll - -Approves or revokes an operator to manage all tokens of the caller. - - -{`function setApprovalForAll(address _operator, bool _approved) external;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token from one address to another. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token, checking for receiver contract compatibility. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} - - -**Parameters:** - - - ---- -### safeTransferFrom - -Safely transfers a token with additional data. - - -{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} - - -**Parameters:** - - - -## Events - - - - -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- - -
- Signature: - -{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} - -
- -
-
- -## Errors - - - - -
- Signature: - -error ERC721InvalidOwner(address _owner); - -
-
- - -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- - -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- - -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- - -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- - -
- Signature: - -error ERC721InvalidApprover(address _approver); - -
-
- - -
- Signature: - -error ERC721InvalidOperator(address _operator); - -
-
- - -
- Signature: - -error ERC721OutOfBoundsIndex(address _owner, uint256 _index); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableFacet} from "@compose-protocol/diamond-contracts/facets/ERC721/ERC721EnumerableFacet.sol"; - -contract MyDiamondConsumer { - IERC721EnumerableFacet immutable erc721EnumerableFacet; - - constructor(address diamondAddress) { - erc721EnumerableFacet = IERC721EnumerableFacet(diamondAddress); - } - - function getTokenSupply() public view returns (uint256) { - return erc721EnumerableFacet.totalSupply(); - } - - function getOwnerOfToken(uint256 tokenId) public view returns (address) { - return erc721EnumerableFacet.ownerOf(tokenId); - } - - function getTokensByIndex(address owner, uint256 index) public view returns (uint256) { - return erc721EnumerableFacet.tokenOfOwnerByIndex(owner, index); - } -}`} - - -## Best Practices - - -- Utilize `tokenOfOwnerByIndex` for iterating through an owner's tokens when the exact number is unknown, after checking `balanceOf`. -- Ensure proper access control is implemented at the diamond level for functions like `transferFrom` and `safeTransferFrom`. -- When upgrading, ensure the storage layout of `ERC721EnumerableFacet` is maintained or handled according to Compose's upgrade guidelines. - - -## Security Considerations - - -The `internalTransferFrom` function is intended for internal use by other facets and should not be called directly externally. Ensure that external calls to `transferFrom` and `safeTransferFrom` are appropriately guarded by access control mechanisms at the diamond proxy level to prevent unauthorized transfers. Reentrancy is mitigated by the standard ERC-721 patterns, but careful review is advised if custom logic interacts with token transfers. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx deleted file mode 100644 index 4227d544..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx +++ /dev/null @@ -1,351 +0,0 @@ ---- -sidebar_position: 99 -title: "ERC721EnumerableMod" -description: "Manages enumerable ERC-721 token state within a diamond." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages enumerable ERC-721 token state within a diamond. - - - -- Manages token enumeration for ERC-721 compliant diamonds. -- Integrates seamlessly with diamond storage for persistent token tracking. -- Provides core logic for minting, burning, and transferring enumerable tokens. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The ERC721EnumerableMod provides essential internal logic for managing ERC-721 token ownership and enumeration. It ensures tokens are correctly added and removed from internal tracking lists during minting and burning operations, enabling efficient querying of token balances and ownership within the diamond. - ---- - -## Storage - -### ERC721EnumerableStorage - - -{`struct ERC721EnumerableStorage { - mapping(uint256 tokenId => address owner) ownerOf; - mapping(address owner => uint256[] ownerTokens) ownerTokens; - mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; - uint256[] allTokens; - mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; - mapping(uint256 tokenId => address approved) approved; - string name; - string symbol; - string baseURI; -}`} - - -### State Variables - - - -## Functions - -### burn - -Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. - - -{`function burn(uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - ---- -### getStorage - -Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. - - -{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} - - -**Returns:** - - - ---- -### mint - -Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. - - -{`function mint(address _to, uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### transferFrom - -Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. - - -{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} - - -**Parameters:** - - - -## Events - - - -
- Emitted when ownership of a token changes, including minting and burning. -
- -
- Signature: - -{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} - -
- -
- Parameters: - -
-
-
- -## Errors - - - -
- Thrown when the sender is not the owner of the token. -
- -
- Signature: - -error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); - -
-
- -
- Thrown when an operator lacks approval to manage a token. -
- -
- Signature: - -error ERC721InsufficientApproval(address _operator, uint256 _tokenId); - -
-
- -
- Thrown when the receiver address is invalid. -
- -
- Signature: - -error ERC721InvalidReceiver(address _receiver); - -
-
- -
- Thrown when the sender address is invalid. -
- -
- Signature: - -error ERC721InvalidSender(address _sender); - -
-
- -
- Thrown when attempting to interact with a non-existent token. -
- -
- Signature: - -error ERC721NonexistentToken(uint256 _tokenId); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IERC721EnumerableMod, IERC721EnumerableStorage} from "./interfaces/IERC721EnumerableMod.sol"; - -contract MyERC721Facet { - // Assume ERC721EnumerableMod is deployed and its address is known - address immutable _erc721EnumerableModAddress; - - constructor(address erc721EnumerableModAddress) { - _erc721EnumerableModAddress = erc721EnumerableModAddress; - } - - function mintToken(address to, uint256 tokenId) external { - (bool success, ) = _erc721EnumerableModAddress.call(abi.encodeWithSignature(\"mint(address,uint256)\", to, tokenId)); - require(success, \"ERC721EnumerableMod: mint failed\"); - } - - function burnToken(uint256 tokenId) external { - (bool success, ) = _erc721EnumerableModAddress.call(abi.encodeWithSignature(\"burn(uint256)\", tokenId)); - require(success, \"ERC721EnumerableMod: burn failed\"); - } - - function transferToken(address from, address to, uint256 tokenId) external { - (bool success, ) = _erc721EnumerableModAddress.call(abi.encodeWithSignature(\"transferFrom(address,address,uint256)\", from, to, tokenId)); - require(success, \"ERC721EnumerableMod: transferFrom failed\"); - } - - function getEnumerableStorage() external view returns (IERC721EnumerableStorage memory) { - (bool success, bytes memory data) = _erc721EnumerableModAddress.staticcall(abi.encodeWithSignature(\"getStorage()\")); - require(success, \"ERC721EnumerableMod: getStorage failed\"); - return abi.decode(data, (IERC721EnumerableStorage)); - } -}`} - - -## Best Practices - - -- Ensure proper access control is implemented in the calling facet before invoking mint, burn, or transferFrom functions to prevent unauthorized state changes. -- Always validate token existence and ownership within the facet before calling module functions that operate on specific tokens. -- Handle potential revert reasons from module functions (e.g., ERC721NonexistentToken, ERC721IncorrectOwner) gracefully in your facet logic. - - -## Integration Notes - - -This module interacts with diamond storage at a predefined slot to maintain its state. Facets calling this module must ensure they do not conflict with this storage slot. The module's internal storage is accessed via inline assembly in `getStorage`, making its state directly queryable by facets. Changes made via `mint`, `burn`, and `transferFrom` are persistent and reflected in the diamond's overall state. - - -
- -
- - diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json deleted file mode 100644 index fdc633f9..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-721 Enumerable", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC721/ERC721Enumerable/index" - } -} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx deleted file mode 100644 index 702eb1cb..00000000 --- a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: "ERC-721 Enumerable" -description: "ERC-721 Enumerable extension for ERC-721 tokens." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-721 Enumerable extension for ERC-721 tokens. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json deleted file mode 100644 index 8ee4f288..00000000 --- a/website/docs/library/token/ERC721/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "ERC-721", - "position": 2, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/ERC721/index" - } -} diff --git a/website/docs/library/token/ERC721/index.mdx b/website/docs/library/token/ERC721/index.mdx deleted file mode 100644 index 24a9e4be..00000000 --- a/website/docs/library/token/ERC721/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "ERC-721" -description: "ERC-721 non-fungible token implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-721 non-fungible token implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx deleted file mode 100644 index 6c9fd6cf..00000000 --- a/website/docs/library/token/Royalty/RoyaltyFacet.mdx +++ /dev/null @@ -1,168 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyFacet" -description: "Manages royalty information for tokens." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyFacet.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Manages royalty information for tokens. - - - -- Implements ERC-2981 `royaltyInfo` function. -- Supports token-specific royalty configurations. -- Provides a fallback to a default royalty setting. - - -## Overview - -The RoyaltyFacet implements the ERC-2981 standard, enabling royalty payments on token sales. It allows for setting token-specific royalties and provides a fallback to a default royalty configuration. This facet surfaces the royalty information necessary for marketplaces and other integrators. - ---- - -## Storage - -### RoyaltyInfo - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### royaltyInfo - -Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) - external - view - returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyFacet} from "@compose/contracts/facets/Royalty/IRoyaltyFacet.sol"; - -contract RoyaltyConsumer { - address immutable diamondAddress; - bytes4 private constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; - - constructor(address _diamondAddress) { - diamondAddress = _diamondAddress; - } - - function getRoyaltyDetails(uint256 _tokenId, uint256 _salePrice) public view returns (address receiver, uint256 royaltyAmount) { - (bool success, bytes memory data) = diamondAddress.call(abi.encodeWithSelector(ROYALTY_INFO_SELECTOR, _tokenId, _salePrice)); - require(success, "RoyaltyFacet: royaltyInfo call failed"); - (receiver, royaltyAmount) = abi.decode(data, (address, uint256)); - return (receiver, royaltyAmount); - } -}`} - - -## Best Practices - - -- Initialize the royalty configuration (default and token-specific) via an appropriate admin facet or deployment script. -- Ensure the `royaltyInfo` function is correctly routed to this facet by the diamond proxy. - - -## Security Considerations - - -The `royaltyInfo` function is read-only and does not pose reentrancy risks. Access control for setting royalty configurations should be managed by other facets responsible for administrative operations. Ensure correct routing to this facet to prevent unexpected behavior. - - -
- -
- - diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx deleted file mode 100644 index 9a0d446c..00000000 --- a/website/docs/library/token/Royalty/RoyaltyMod.mdx +++ /dev/null @@ -1,346 +0,0 @@ ---- -sidebar_position: 99 -title: "RoyaltyMod" -description: "ERC-2981 royalty logic for NFTs." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -ERC-2981 royalty logic for NFTs. - - - -- Implements ERC-2981 standard for NFT royalties. -- Supports both default royalties applicable to all tokens and token-specific overrides. -- Provides functions to query royalty information, falling back to defaults when token-specific data is absent. -- Includes validation for royalty receivers and fee percentages to ensure correct configuration. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -Implements the ERC-2981 royalty standard, enabling NFTs to specify royalty payments on secondary sales. This module provides functions to set, retrieve, and manage both default and token-specific royalty information, ensuring adherence to the standard and allowing for flexible royalty configurations within a diamond. - ---- - -## Storage - -### RoyaltyInfo - -Structure containing royalty information. **Properties** - - -{`struct RoyaltyInfo { - address receiver; - uint96 royaltyFraction; -}`} - - ---- -### RoyaltyStorage - -storage-location: erc8042:compose.erc2981 - - -{`struct RoyaltyStorage { - RoyaltyInfo defaultRoyaltyInfo; - mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; -}`} - - -### State Variables - - - -## Functions - -### deleteDefaultRoyalty - -Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. - - -{`function deleteDefaultRoyalty() ;`} - - ---- -### getStorage - -Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. - - -{`function getStorage() pure returns (RoyaltyStorage storage s);`} - - -**Returns:** - - - ---- -### resetTokenRoyalty - -Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. - - -{`function resetTokenRoyalty(uint256 _tokenId) ;`} - - -**Parameters:** - - - ---- -### royaltyInfo - -Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. - - -{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} - - -**Parameters:** - - - -**Returns:** - - - ---- -### setDefaultRoyalty - -Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. - - -{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - ---- -### setTokenRoyalty - -Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. - - -{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} - - -**Parameters:** - - - -## Errors - - - -
- Thrown when default royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when default royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); - -
-
- -
- Thrown when token-specific royalty fee exceeds 100% (10000 basis points). -
- -
- Signature: - -error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); - -
-
- -
- Thrown when token-specific royalty receiver is the zero address. -
- -
- Signature: - -error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {IRoyaltyMod} from "./interfaces/IRoyaltyMod.sol"; - -contract RoyaltyFacet { - // Assume IRoyaltyMod is correctly imported and diamond storage is accessible. - // The actual diamond storage slot for RoyaltyMod is managed by the diamond proxy. - - function exampleSetDefaultRoyalty(address _receiver, uint16 _feeNumerator, uint16 _feeDenominator) external { - IRoyaltyMod royaltyMod = IRoyaltyMod(address(this)); // Assuming facet is called on diamond proxy - // The fee is calculated as (_feeNumerator / _feeDenominator) * salePrice - royaltyMod.setDefaultRoyalty(_receiver, _feeNumerator, _feeDenominator); - } - - function exampleSetTokenRoyalty(uint256 _tokenId, address _receiver, uint16 _feeNumerator, uint16 _feeDenominator) external { - IRoyaltyMod royaltyMod = IRoyaltyMod(address(this)); - royaltyMod.setTokenRoyalty(_tokenId, _receiver, _feeNumerator, _feeDenominator); - } - - function exampleGetRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { - IRoyaltyMod royaltyMod = IRoyaltyMod(address(this)); - (receiver, royaltyAmount) = royaltyMod.royaltyInfo(_tokenId, _salePrice); - return (receiver, royaltyAmount); - } -}`} - - -## Best Practices - - -- Use `setDefaultRoyalty` for broad application and `setTokenRoyalty` for specific exceptions to manage royalty configurations efficiently. -- Validate the `_receiver` address to prevent sending royalties to an invalid or unintended address, leveraging the module's built-in error checks (e.g., `ERC2981InvalidDefaultRoyaltyReceiver`). -- Be aware that calling `resetTokenRoyalty` will revert token-specific royalties, causing the `royaltyInfo` function to fall back to the default royalty settings. - - -## Integration Notes - - -The RoyaltyMod utilizes a predefined storage slot within the diamond's storage contract to store its state, including default royalty information and token-specific royalty mappings. Facets interacting with this module should call its functions through the diamond proxy. The `getStorage` function can be used to access the module's internal storage struct directly if needed for inspection, though direct manipulation is discouraged. Changes to default or token-specific royalties are immediately reflected in subsequent calls to `royaltyInfo`. - - -
- -
- - diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json deleted file mode 100644 index cb6b460f..00000000 --- a/website/docs/library/token/Royalty/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Royalty", - "position": 5, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/Royalty/index" - } -} diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx deleted file mode 100644 index deb4f513..00000000 --- a/website/docs/library/token/Royalty/index.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: "Royalty" -description: "ERC-2981 royalty standard implementations." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - ERC-2981 royalty standard implementations. - - - - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json deleted file mode 100644 index 3f26c2ce..00000000 --- a/website/docs/library/token/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Token Standards", - "position": 3, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/token/index" - } -} diff --git a/website/docs/library/token/index.mdx b/website/docs/library/token/index.mdx deleted file mode 100644 index 17b1ae16..00000000 --- a/website/docs/library/token/index.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: "Token Standards" -description: "Token standard implementations for Compose diamonds." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Token standard implementations for Compose diamonds. - - - - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - } - size="medium" - /> - diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx deleted file mode 100644 index 46fb6f55..00000000 --- a/website/docs/library/utils/NonReentrancyMod.mdx +++ /dev/null @@ -1,143 +0,0 @@ ---- -sidebar_position: 99 -title: "NonReentrancyMod" -description: "Enforce non-reentrant execution within facets." -gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/libraries/NonReentrancyMod.sol" ---- - -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Badge from '@site/src/components/ui/Badge'; -import Callout from '@site/src/components/ui/Callout'; -import CalloutBox from '@site/src/components/ui/CalloutBox'; -import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; -import PropertyTable from '@site/src/components/api/PropertyTable'; -import ExpandableCode from '@site/src/components/code/ExpandableCode'; -import CodeShowcase from '@site/src/components/code/CodeShowcase'; -import RelatedDocs from '@site/src/components/docs/RelatedDocs'; -import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; -import LastUpdated from '@site/src/components/docs/LastUpdated'; -import ReadingTime from '@site/src/components/docs/ReadingTime'; -import GradientText from '@site/src/components/ui/GradientText'; -import GradientButton from '@site/src/components/ui/GradientButton'; - - -Enforce non-reentrant execution within facets. - - - -- Prevents reentrant function calls, enhancing security. -- Simple and explicit `enter`/`exit` pattern for easy integration. -- Utilizes a single storage slot for the reentrancy guard. - - - -This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. - - -## Overview - -The NonReentrancyMod provides essential functions to prevent reentrant calls, ensuring the integrity and predictable execution of your diamond's facets. By managing reentrancy guards, this module safeguards against common vulnerabilities and unexpected state changes during complex transactions. - ---- - -## Storage - -### State Variables - - - -## Functions - -### enter - -How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function - - -{`function enter() ;`} - - ---- -### exit - -This locks the entry into a function - - -{`function exit() ;`} - - -## Errors - - - -
- Function selector - 0x43a0d067 -
- -
- Signature: - -error Reentrancy(); - -
-
-
- -## Usage Example - - -{`pragma solidity ^0.8.30; - -import {LibNonReentrancy} from "@compose/diamond-proxy/contracts/modules/nonReentrancy/LibNonReentrancy.sol"; - -contract MyFacet { - using LibNonReentrancy for uint256; - - uint256 private _reentrancyGuard; - - function sensitiveOperation() external { - // Lock reentrancy before executing sensitive logic - _reentrancyGuard.enter(); - - // ... sensitive logic ... - - // Unlock reentrancy after execution - _reentrancyGuard.exit(); - } - - function _beforeAll() internal override { - // Initialize the reentrancy guard in the diamond storage - // Assuming diamond storage has a slot for the guard, e.g., \`uint256 reentrancyGuard;\` - // This initialization would typically happen once during diamond deployment or upgrade. - // For example, if \`LibNonReentrancy.storage(LibNonReentrancy.nonReentrancyStorageSlot()).reentrancyGuard\` is accessible: - // LibNonReentrancy.storage(LibNonReentrancy.nonReentrancyStorageSlot()).reentrancyGuard = 1; // Initialized state - } -}`} - - -## Best Practices - - -- Always call `enter()` at the beginning and `exit()` at the end of any function susceptible to reentrancy. -- Ensure the reentrancy guard is correctly initialized within the diamond's storage during deployment or upgrade. - - -## Integration Notes - - -This module manages a reentrancy guard, typically stored as a `uint256` within the diamond's shared storage. The `enter()` function increments the guard, and `exit()` decrements it. A non-zero guard indicates the function is currently executing, preventing reentrant calls. Ensure the diamond's storage layout accommodates this guard, and that it is initialized to a non-zero value (e.g., 1) to signify the initial locked state before any function call. The `LibNonReentrancy.storage()` helper function is used to access the guard variable within the diamond's storage. - - -
- -
- - diff --git a/website/docs/library/utils/_category_.json b/website/docs/library/utils/_category_.json deleted file mode 100644 index d9c087be..00000000 --- a/website/docs/library/utils/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "Utilities", - "position": 4, - "collapsible": true, - "collapsed": true, - "link": { - "type": "doc", - "id": "library/utils/index" - } -} diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx deleted file mode 100644 index 6722a23b..00000000 --- a/website/docs/library/utils/index.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "Utilities" -description: "Utility libraries and helpers for diamond development." -sidebar_class_name: hidden ---- - -import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; -import DocSubtitle from '@site/src/components/docs/DocSubtitle'; -import Icon from '@site/src/components/ui/Icon'; - - - Utility libraries and helpers for diamond development. - - - - } - size="medium" - /> - From e3c2c76f47d426ee4e994068b80df2c913fdae06 Mon Sep 17 00:00:00 2001 From: MN Date: Mon, 22 Dec 2025 19:24:45 -0500 Subject: [PATCH 67/68] add check --- .github/workflows/docs-generate.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs-generate.yml b/.github/workflows/docs-generate.yml index 89fb42b0..edf96990 100644 --- a/.github/workflows/docs-generate.yml +++ b/.github/workflows/docs-generate.yml @@ -166,7 +166,10 @@ jobs: git reset # Only stage website documentation files (force add in case they're ignored) - git add -f website/docs/contracts/ + # Use library directory (the actual output directory) instead of contracts + if [ -d "website/docs/library" ]; then + git add -f website/docs/library/ + fi - name: Create Pull Request if: steps.check-generated.outputs.has_generated == 'true' From de03fd06beda1f817a4c9a4751fdbea93f8b0770 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Tue, 23 Dec 2025 00:30:07 +0000 Subject: [PATCH 68/68] docs: auto-generate docs pages from NatSpec --- website/docs/library/_category_.json | 10 + .../AccessControl/AccessControlFacet.mdx | 528 ++++++++++++++ .../access/AccessControl/AccessControlMod.mdx | 446 ++++++++++++ .../access/AccessControl/_category_.json | 10 + .../library/access/AccessControl/index.mdx | 30 + .../AccessControlPausableFacet.mdx | 331 +++++++++ .../AccessControlPausableMod.mdx | 379 ++++++++++ .../AccessControlPausable/_category_.json | 10 + .../access/AccessControlPausable/index.mdx | 30 + .../AccessControlTemporalFacet.mdx | 405 +++++++++++ .../AccessControlTemporalMod.mdx | 474 ++++++++++++ .../AccessControlTemporal/_category_.json | 10 + .../access/AccessControlTemporal/index.mdx | 30 + .../docs/library/access/Owner/OwnerFacet.mdx | 197 +++++ .../docs/library/access/Owner/OwnerMod.mdx | 253 +++++++ .../docs/library/access/Owner/_category_.json | 10 + website/docs/library/access/Owner/index.mdx | 30 + .../OwnerTwoSteps/OwnerTwoStepsFacet.mdx | 201 +++++ .../access/OwnerTwoSteps/OwnerTwoStepsMod.mdx | 302 ++++++++ .../access/OwnerTwoSteps/_category_.json | 10 + .../library/access/OwnerTwoSteps/index.mdx | 30 + website/docs/library/access/_category_.json | 10 + website/docs/library/access/index.mdx | 51 ++ .../docs/library/diamond/DiamondCutFacet.mdx | 308 ++++++++ .../docs/library/diamond/DiamondCutMod.mdx | 393 ++++++++++ .../library/diamond/DiamondInspectFacet.mdx | 156 ++++ .../library/diamond/DiamondLoupeFacet.mdx | 247 +++++++ website/docs/library/diamond/DiamondMod.mdx | 233 ++++++ website/docs/library/diamond/_category_.json | 10 + .../diamond/example/ExampleDiamond.mdx | 139 ++++ .../library/diamond/example/_category_.json | 10 + .../docs/library/diamond/example/index.mdx | 23 + website/docs/library/diamond/index.mdx | 58 ++ website/docs/library/index.mdx | 51 ++ .../interfaceDetection/ERC165/ERC165Facet.mdx | 152 ++++ .../interfaceDetection/ERC165/ERC165Mod.mdx | 154 ++++ .../interfaceDetection/ERC165/_category_.json | 10 + .../interfaceDetection/ERC165/index.mdx | 30 + .../interfaceDetection/_category_.json | 10 + .../docs/library/interfaceDetection/index.mdx | 23 + .../library/token/ERC1155/ERC1155Facet.mdx | 653 +++++++++++++++++ .../docs/library/token/ERC1155/ERC1155Mod.mdx | 601 +++++++++++++++ .../library/token/ERC1155/_category_.json | 10 + website/docs/library/token/ERC1155/index.mdx | 30 + .../token/ERC20/ERC20/ERC20BurnFacet.mdx | 232 ++++++ .../library/token/ERC20/ERC20/ERC20Facet.mdx | 545 ++++++++++++++ .../library/token/ERC20/ERC20/ERC20Mod.mdx | 430 +++++++++++ .../library/token/ERC20/ERC20/_category_.json | 10 + .../docs/library/token/ERC20/ERC20/index.mdx | 37 + .../ERC20Bridgeable/ERC20BridgeableFacet.mdx | 390 ++++++++++ .../ERC20Bridgeable/ERC20BridgeableMod.mdx | 421 +++++++++++ .../ERC20/ERC20Bridgeable/_category_.json | 10 + .../token/ERC20/ERC20Bridgeable/index.mdx | 30 + .../ERC20/ERC20Permit/ERC20PermitFacet.mdx | 339 +++++++++ .../ERC20/ERC20Permit/ERC20PermitMod.mdx | 281 +++++++ .../token/ERC20/ERC20Permit/_category_.json | 10 + .../library/token/ERC20/ERC20Permit/index.mdx | 30 + .../docs/library/token/ERC20/_category_.json | 10 + website/docs/library/token/ERC20/index.mdx | 37 + .../token/ERC6909/ERC6909/ERC6909Facet.mdx | 513 +++++++++++++ .../token/ERC6909/ERC6909/ERC6909Mod.mdx | 525 ++++++++++++++ .../token/ERC6909/ERC6909/_category_.json | 10 + .../library/token/ERC6909/ERC6909/index.mdx | 30 + .../library/token/ERC6909/_category_.json | 10 + website/docs/library/token/ERC6909/index.mdx | 23 + .../token/ERC721/ERC721/ERC721BurnFacet.mdx | 200 +++++ .../token/ERC721/ERC721/ERC721Facet.mdx | 615 ++++++++++++++++ .../library/token/ERC721/ERC721/ERC721Mod.mdx | 362 +++++++++ .../token/ERC721/ERC721/_category_.json | 10 + .../library/token/ERC721/ERC721/index.mdx | 37 + .../ERC721EnumerableBurnFacet.mdx | 202 ++++++ .../ERC721EnumerableFacet.mdx | 686 ++++++++++++++++++ .../ERC721Enumerable/ERC721EnumerableMod.mdx | 338 +++++++++ .../ERC721/ERC721Enumerable/_category_.json | 10 + .../token/ERC721/ERC721Enumerable/index.mdx | 37 + .../docs/library/token/ERC721/_category_.json | 10 + website/docs/library/token/ERC721/index.mdx | 30 + .../library/token/Royalty/RoyaltyFacet.mdx | 171 +++++ .../docs/library/token/Royalty/RoyaltyMod.mdx | 340 +++++++++ .../library/token/Royalty/_category_.json | 10 + website/docs/library/token/Royalty/index.mdx | 30 + website/docs/library/token/_category_.json | 10 + website/docs/library/token/index.mdx | 51 ++ .../docs/library/utils/NonReentrancyMod.mdx | 135 ++++ website/docs/library/utils/_category_.json | 10 + website/docs/library/utils/index.mdx | 23 + 86 files changed, 14328 insertions(+) create mode 100644 website/docs/library/_category_.json create mode 100644 website/docs/library/access/AccessControl/AccessControlFacet.mdx create mode 100644 website/docs/library/access/AccessControl/AccessControlMod.mdx create mode 100644 website/docs/library/access/AccessControl/_category_.json create mode 100644 website/docs/library/access/AccessControl/index.mdx create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx create mode 100644 website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx create mode 100644 website/docs/library/access/AccessControlPausable/_category_.json create mode 100644 website/docs/library/access/AccessControlPausable/index.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx create mode 100644 website/docs/library/access/AccessControlTemporal/_category_.json create mode 100644 website/docs/library/access/AccessControlTemporal/index.mdx create mode 100644 website/docs/library/access/Owner/OwnerFacet.mdx create mode 100644 website/docs/library/access/Owner/OwnerMod.mdx create mode 100644 website/docs/library/access/Owner/_category_.json create mode 100644 website/docs/library/access/Owner/index.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx create mode 100644 website/docs/library/access/OwnerTwoSteps/_category_.json create mode 100644 website/docs/library/access/OwnerTwoSteps/index.mdx create mode 100644 website/docs/library/access/_category_.json create mode 100644 website/docs/library/access/index.mdx create mode 100644 website/docs/library/diamond/DiamondCutFacet.mdx create mode 100644 website/docs/library/diamond/DiamondCutMod.mdx create mode 100644 website/docs/library/diamond/DiamondInspectFacet.mdx create mode 100644 website/docs/library/diamond/DiamondLoupeFacet.mdx create mode 100644 website/docs/library/diamond/DiamondMod.mdx create mode 100644 website/docs/library/diamond/_category_.json create mode 100644 website/docs/library/diamond/example/ExampleDiamond.mdx create mode 100644 website/docs/library/diamond/example/_category_.json create mode 100644 website/docs/library/diamond/example/index.mdx create mode 100644 website/docs/library/diamond/index.mdx create mode 100644 website/docs/library/index.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx create mode 100644 website/docs/library/interfaceDetection/ERC165/_category_.json create mode 100644 website/docs/library/interfaceDetection/ERC165/index.mdx create mode 100644 website/docs/library/interfaceDetection/_category_.json create mode 100644 website/docs/library/interfaceDetection/index.mdx create mode 100644 website/docs/library/token/ERC1155/ERC1155Facet.mdx create mode 100644 website/docs/library/token/ERC1155/ERC1155Mod.mdx create mode 100644 website/docs/library/token/ERC1155/_category_.json create mode 100644 website/docs/library/token/ERC1155/index.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20/index.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx create mode 100644 website/docs/library/token/ERC20/ERC20Permit/_category_.json create mode 100644 website/docs/library/token/ERC20/ERC20Permit/index.mdx create mode 100644 website/docs/library/token/ERC20/_category_.json create mode 100644 website/docs/library/token/ERC20/index.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx create mode 100644 website/docs/library/token/ERC6909/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC6909/ERC6909/index.mdx create mode 100644 website/docs/library/token/ERC6909/_category_.json create mode 100644 website/docs/library/token/ERC6909/index.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721/index.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/_category_.json create mode 100644 website/docs/library/token/ERC721/ERC721Enumerable/index.mdx create mode 100644 website/docs/library/token/ERC721/_category_.json create mode 100644 website/docs/library/token/ERC721/index.mdx create mode 100644 website/docs/library/token/Royalty/RoyaltyFacet.mdx create mode 100644 website/docs/library/token/Royalty/RoyaltyMod.mdx create mode 100644 website/docs/library/token/Royalty/_category_.json create mode 100644 website/docs/library/token/Royalty/index.mdx create mode 100644 website/docs/library/token/_category_.json create mode 100644 website/docs/library/token/index.mdx create mode 100644 website/docs/library/utils/NonReentrancyMod.mdx create mode 100644 website/docs/library/utils/_category_.json create mode 100644 website/docs/library/utils/index.mdx diff --git a/website/docs/library/_category_.json b/website/docs/library/_category_.json new file mode 100644 index 00000000..04125e1e --- /dev/null +++ b/website/docs/library/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Library", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/index" + } +} diff --git a/website/docs/library/access/AccessControl/AccessControlFacet.mdx b/website/docs/library/access/AccessControl/AccessControlFacet.mdx new file mode 100644 index 00000000..1cf4cc78 --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlFacet.mdx @@ -0,0 +1,528 @@ +--- +sidebar_position: 99 +title: "AccessControlFacet" +description: "Manage roles and permissions within a Compose diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within a Compose diamond. + + + +- Role-based access control (RBAC). +- Hierarchical role administration. +- Permission management for individual accounts. +- Batch operations for efficient role assignments and revocations. + + +## Overview + +The AccessControlFacet provides a robust role-based access control (RBAC) system for Compose diamonds. It allows for granular permission management, enabling fine-grained control over who can perform specific actions. This facet is crucial for securing administrative functions and ensuring proper governance. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### hasRole + +Returns if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +Checks if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +--- +### getRoleAdmin + +Returns the admin role for a role. + + +{`function getRoleAdmin(bytes32 _role) external view returns (bytes32);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +Sets the admin role for a role. Emits a RoleAdminChanged event. Reverts with AccessControlUnauthorizedAccount If the caller is not the current admin of the role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) external;`} + + +**Parameters:** + + + +--- +### grantRole + +Grants a role to an account. Emits a RoleGranted event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### revokeRole + +Revokes a role from an account. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### grantRoleBatch + +Grants a role to multiple accounts in a single transaction. Emits a RoleGranted event for each newly granted account. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### revokeRoleBatch + +Revokes a role from multiple accounts in a single transaction. Emits a RoleRevoked event for each account the role is revoked from. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeRoleBatch(bytes32 _role, address[] calldata _accounts) external;`} + + +**Parameters:** + + + +--- +### renounceRole + +Renounces a role from the caller. Emits a RoleRevoked event. Reverts with AccessControlUnauthorizedSender If the caller is not the account to renounce the role from. + + +{`function renounceRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when the sender is not the account to renounce the role from. +
+ +
+ Signature: + +error AccessControlUnauthorizedSender(address _sender, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet, DiamondLoupeFacet, FacetCutAction} from "@compose-protocol/diamond-contracts/contracts/facets/DiamondCutFacet.sol"; +import {AccessControlFacet} from "@compose-protocol/diamond-contracts/contracts/facets/AccessControlFacet.sol"; + +contract MyDiamond is DiamondCutFacet, DiamondLoupeFacet { + constructor(address _diamondAdmin) DiamondCutFacet(_diamondAdmin) {} + + // Function to add facets to the diamond (e.g., AccessControlFacet) + function upgrade() external { + // Assume AccessControlFacet is deployed at address + // and its ABI is known. + address accessControlFacetAddress = address(0x123...); + bytes calldata selector = abi.encodeWithSelector(AccessControlFacet.grantRole.selector); + + FacetCut[] memory cut = new FacetCut[](1); + cut[0] = FacetCut(accessControlFacetAddress, FacetCutAction.Add, new bytes4[](1)); + cut[0].functionSelectors[0] = selector; + + diamondCut(cut, address(0), ""); + } + + // Example of calling a function that requires a role + function grantAdminRoleTo(address _account) external { + // Assuming DEFAULT_ADMIN_ROLE is defined and accessible + AccessControlFacet(address(this)).grantRole(AccessControlFacet.DEFAULT_ADMIN_ROLE, _account); + } +} +`} + + +## Best Practices + + +- Integrate the AccessControlFacet early in the diamond deployment to establish administrative roles. +- Define custom roles specific to your diamond's logic and manage their administration carefully. +- Use batch functions (`grantRoleBatch`, `revokeRoleBatch`) for efficiency when managing multiple accounts or roles. + + +## Security Considerations + + +Ensure that the `DEFAULT_ADMIN_ROLE` is granted only to trusted addresses during deployment. Carefully manage role assignments to prevent unauthorized access to critical functions. The `renounceRole` function should be used with caution, as it permanently removes the caller's access to the role. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/AccessControlMod.mdx b/website/docs/library/access/AccessControl/AccessControlMod.mdx new file mode 100644 index 00000000..4db51ccc --- /dev/null +++ b/website/docs/library/access/AccessControl/AccessControlMod.mdx @@ -0,0 +1,446 @@ +--- +sidebar_position: 99 +title: "AccessControlMod" +description: "Manage roles and permissions within a diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControl/AccessControlMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage roles and permissions within a diamond. + + + +- Role-based access control for granular permission management. +- Functions for granting, revoking, and checking role assignments. +- Ability to set and manage administrative roles for other roles. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The AccessControl module provides a robust system for managing roles and permissions. It allows for granular control over which accounts can perform specific actions by assigning roles. This is crucial for building secure and upgradeable diamonds, ensuring that only authorized entities can modify critical state or execute sensitive functions. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns the storage for the AccessControl. + + +{`function getStorage() pure returns (AccessControlStorage storage _s);`} + + +**Returns:** + + + +--- +### grantRole + +function to grant a role to an account. + + +{`function grantRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### hasRole + +function to check if an account has a role. + + +{`function hasRole(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireRole + +function to check if an account has a required role. Reverts with AccessControlUnauthorizedAccount If the account does not have the role. + + +{`function requireRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeRole + +function to revoke a role from an account. + + +{`function revokeRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setRoleAdmin + +function to set the admin role for a role. + + +{`function setRoleAdmin(bytes32 _role, bytes32 _adminRole) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when the admin role for a role is changed. +
+ +
+ Signature: + +{`event RoleAdminChanged(bytes32 indexed _role, bytes32 indexed _previousAdminRole, bytes32 indexed _newAdminRole);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is granted to an account. +
+ +
+ Signature: + +{`event RoleGranted(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a role is revoked from an account. +
+ +
+ Signature: + +{`event RoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControl} from "@compose/contracts/modules/access-control/IAccessControl.sol"; + +contract MyFacet { + IAccessControl public immutable accessControl; + + constructor(address _diamondAddress) { + accessControl = IAccessControl(_diamondAddress); + } + + function grantAdminRole(address _account) external { + // Assuming DEFAULT_ADMIN_ROLE is defined elsewhere or is a constant + // For demonstration, let's assume it's a known role identifier + bytes32 constant DEFAULT_ADMIN_ROLE = keccak256("DEFAULT_ADMIN_ROLE"); + accessControl.grantRole(DEFAULT_ADMIN_ROLE, _account); + } + + function performAdminAction() external { + bytes32 constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + accessControl.requireRole(ADMIN_ROLE, msg.sender); + // ... perform admin action ... + } +}`} + + +## Best Practices + + +- Use `requireRole` to enforce access control checks at the beginning of external functions. +- Define roles using `keccak256("ROLE_NAME")` and manage them consistently. +- Store the `AccessControl` facet address in your diamond proxy for direct interaction. + + +## Integration Notes + + +The AccessControl module utilizes its own dedicated storage slot within the diamond's storage layout. Facets interact with this module through its `IAccessControl` interface. Changes made to role assignments via `grantRole` or `revokeRole` are immediately reflected and can be checked by any facet using `hasRole` or `requireRole`. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControl/_category_.json b/website/docs/library/access/AccessControl/_category_.json new file mode 100644 index 00000000..1504700a --- /dev/null +++ b/website/docs/library/access/AccessControl/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/AccessControl/index" + } +} diff --git a/website/docs/library/access/AccessControl/index.mdx b/website/docs/library/access/AccessControl/index.mdx new file mode 100644 index 00000000..5031c709 --- /dev/null +++ b/website/docs/library/access/AccessControl/index.mdx @@ -0,0 +1,30 @@ +--- +title: "Access Control" +description: "Role-based access control (RBAC) pattern." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Role-based access control (RBAC) pattern. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx new file mode 100644 index 00000000..18dc30da --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableFacet.mdx @@ -0,0 +1,331 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableFacet" +description: "Manage role-based access control and pause functionality." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage role-based access control and pause functionality. + + + +- Role-specific pausing and unpausing of functionality. +- Reverts with specific errors for unauthorized access and paused roles. +- Provides view functions to check role pause status. + + +## Overview + +This facet integrates role-based access control with pausing capabilities for specific roles. It allows administrators to temporarily disable roles, preventing any account from executing functions associated with a paused role. This provides granular control over operations during sensitive periods or maintenance. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +### State Variables + + + +## Functions + +### isRolePaused + +Returns if a role is paused. + + +{`function isRolePaused(bytes32 _role) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +Temporarily disables a role, preventing all accounts from using it. Only the admin of the role can pause it. Emits a RolePaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function pauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### unpauseRole + +Re-enables a role that was previously paused. Only the admin of the role can unpause it. Emits a RoleUnpaused event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function unpauseRole(bytes32 _role) external;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +Checks if an account has a role and if the role is not paused. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableFacet} from "@compose/contracts/facets/AccessControlPausable/IAccessControlPausableFacet.sol"; +import {IDiamondLoupe} from "@compose/contracts/facets/DiamondLoupe/IDiamondLoupe.sol"; + +contract AccessControlPausableConsumer { + address immutable DIAMOND_ADDRESS; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function pauseMyRole() external { + bytes4 selector = IAccessControlPausableFacet.pauseRole.selector; + (bool success, ) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _MY_ROLE_ID)); + require(success, "Failed to pause role"); + } + + function isMyRolePaused() external view returns (bool) { + bytes4 selector = IAccessControlPausableFacet.isRolePaused.selector; + (bool success, bytes memory data) = DIAMOND_ADDRESS.call(abi.encodeWithSelector(selector, _MY_ROLE_ID)); + require(success, "Failed to check role paused status"); + return abi.decode(data, (bool)); + } + + // Assume _MY_ROLE_ID is defined elsewhere + uint256 private constant _MY_ROLE_ID = 1; +}`} + + +## Best Practices + + +- Ensure the caller has the appropriate administrative role before attempting to pause or unpause a role. +- Use `requireRoleNotPaused` within other facets or contract logic that relies on specific roles to enforce pause states. +- Store role IDs and administrative mappings securely within the diamond's storage. + + +## Security Considerations + + +Access to `pauseRole` and `unpauseRole` functions is restricted to the administrator of the respective role, preventing unauthorized pausing. The `requireRoleNotPaused` function ensures that operations tied to a role cannot be executed while that role is paused, mitigating risks during downtime or maintenance. Ensure role administration is managed securely to prevent accidental or malicious pausing. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx new file mode 100644 index 00000000..7ae20ba3 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/AccessControlPausableMod.mdx @@ -0,0 +1,379 @@ +--- +sidebar_position: 99 +title: "AccessControlPausableMod" +description: "Manage role-based pausing for diamond functionality." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlPausable/AccessControlPausableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage role-based pausing for diamond functionality. + + + +- Role-specific pausing: Allows granular control over which operations are paused based on assigned roles. +- Integration with Access Control: Leverages existing role management for authorization checks. +- Reverts with specific errors: Differentiates between unauthorized access and paused roles for clearer debugging. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides role-based pausing capabilities, allowing specific roles to halt or resume certain operations within a diamond. It integrates with the AccessControl facet to enforce role checks before pausing or unpausing. This ensures that only authorized accounts can control the pause state of critical functions, enhancing operational safety and control. + +--- + +## Storage + +### AccessControlPausableStorage + + +{`struct AccessControlPausableStorage { + mapping(bytes32 role => bool paused) pausedRoles; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlPausable. + + +{`function getStorage() pure returns (AccessControlPausableStorage storage s);`} + + +**Returns:** + + + +--- +### isRolePaused + +function to check if a role is paused. + + +{`function isRolePaused(bytes32 _role) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### pauseRole + +function to pause a role. + + +{`function pauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +--- +### requireRoleNotPaused + +function to check if an account has a role and if the role is not paused. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRolePaused If the role is paused. + + +{`function requireRoleNotPaused(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### unpauseRole + +function to unpause a role. + + +{`function unpauseRole(bytes32 _role) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is paused. +
+ +
+ Signature: + +{`event RolePaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a role is unpaused. +
+ +
+ Signature: + +{`event RoleUnpaused(bytes32 indexed _role, address indexed _account);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role is paused and an operation requiring that role is attempted. +
+ +
+ Signature: + +error AccessControlRolePaused(bytes32 _role); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlPausableMod} from "../interfaces/IAccessControlPausableMod.sol"; + +contract MyFacet { + IAccessControlPausableMod public immutable accessControlPausableMod; + + constructor(address _accessControlPausableMod) { + accessControlPausableMod = IAccessControlPausableMod(_accessControlPausableMod); + } + + function _someRestrictedFunction() internal view { + // Ensure the caller has the 'OPERATOR' role and it's not paused + accessControlPausableMod.requireRoleNotPaused(msg.sender, keccak256("OPERATOR")); + + // ... function logic ... + } + + function _pauseOperatorRole() external { + // Only an admin can pause the OPERATOR role + // (Assuming an admin role is managed by AccessControl facet) + accessControlPausableMod.pauseRole(keccak256("OPERATOR")); + } + + function _unpauseOperatorRole() external { + accessControlPausableMod.unpauseRole(keccak256("OPERATOR")); + } +}`} + + +## Best Practices + + +- Use `requireRoleNotPaused` at the beginning of functions to enforce role presence and ensure the role is not paused before executing sensitive logic. +- Grant role pausing/unpausing permissions judiciously, typically to a highly privileged role like an admin. +- Implement custom errors or descriptive NatSpec for roles to improve clarity in access control checks. + + +## Integration Notes + + +The AccessControlPausableMod interacts with the diamond's storage to manage pause states for roles. Facets can call `requireRoleNotPaused` to enforce that a caller possesses a specific role and that the role is not currently paused. The module relies on the underlying AccessControl facet to manage role assignments. The state of paused roles is maintained within the module's storage, accessible via `getAccessControlStorage` and `getStorage`. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlPausable/_category_.json b/website/docs/library/access/AccessControlPausable/_category_.json new file mode 100644 index 00000000..96418b00 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Pausable Access Control", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/AccessControlPausable/index" + } +} diff --git a/website/docs/library/access/AccessControlPausable/index.mdx b/website/docs/library/access/AccessControlPausable/index.mdx new file mode 100644 index 00000000..e5719126 --- /dev/null +++ b/website/docs/library/access/AccessControlPausable/index.mdx @@ -0,0 +1,30 @@ +--- +title: "Pausable Access Control" +description: "RBAC with pause functionality." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + RBAC with pause functionality. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx new file mode 100644 index 00000000..55738992 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalFacet.mdx @@ -0,0 +1,405 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalFacet" +description: "Manages time-bound role assignments within a diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages time-bound role assignments within a diamond. + + + +- Grants roles with specific expiry timestamps. +- Automatically enforces role expiry, revoking access upon expiration. +- Provides granular control over time-bound permissions. + + +## Overview + +The AccessControlTemporalFacet extends the diamond's access control capabilities by introducing time-bound role assignments. This facet allows administrators to grant roles that automatically expire, enhancing security and operational flexibility. It provides functions to manage these temporal roles and check their validity. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getRoleExpiry + +Returns the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +Checks if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +Grants a role to an account with an expiry timestamp. Only the admin of the role can grant it with expiry. Emits a RoleGrantedWithExpiry event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) external;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +Revokes a temporal role from an account. Only the admin of the role can revoke it. Emits a TemporalRoleRevoked event. Reverts with AccessControlUnauthorizedAccount If the caller is not the admin of the role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) external;`} + + +**Parameters:** + + + +--- +### requireValidRole + +Checks if an account has a valid (non-expired) role. - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( + bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupe} from "@compose/diamond-loupe/src/IDiamondLoupe.sol"; +import {AccessControlTemporalFacet as Facet} from "@compose/access-control-temporal/src/AccessControlTemporalFacet.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +contract MyDiamond is IDiamondLoupe, IERC165 { + // ... other facets + + function getAccessControlTemporalFacet() public view returns (Facet instance) { + bytes4 selector = Facet.grantRoleWithExpiry.selector; + address facetAddress = diamond.facetAddress(selector); + return Facet(facetAddress); + } + + function grantAdminRoleWithExpiry(address _account, bytes32 _role, uint64 _expiry) external { + Facet(diamond.facetAddress(Facet.grantRoleWithExpiry.selector)).grantRoleWithExpiry(_account, _role, _expiry); + } + + function isRoleCurrentlyValid(address _account, bytes32 _role) public view returns (bool) { + return !AccessControlRoleExpired.selector.is mencegahRoleExpired(_account, _role); + } + + // ... other diamond logic +}`} + + +## Best Practices + + +- Initialize temporal roles with appropriate expiry timestamps to prevent indefinite access. +- Regularly audit temporal role grants to ensure they align with current security policies. +- Utilize `revokeTemporalRole` for immediate revocation of expired or unnecessary temporal roles. + + +## Security Considerations + + +Ensure that the caller invoking `grantRoleWithExpiry` and `revokeTemporalRole` possesses the necessary administrative privileges for the target role to prevent unauthorized role management. Input validation for `_expiry` should be handled by the caller or a higher-level access control mechanism to prevent setting invalid or future-dated expiry times incorrectly. The facet relies on the underlying diamond's access control for initial authorization of administrative actions. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx new file mode 100644 index 00000000..1b46e1d7 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/AccessControlTemporalMod.mdx @@ -0,0 +1,474 @@ +--- +sidebar_position: 99 +title: "AccessControlTemporalMod" +description: "Manage time-bound role assignments for diamond access control." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/AccessControlTemporal/AccessControlTemporalMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage time-bound role assignments for diamond access control. + + + +- Grants roles with a specified expiry timestamp. +- Automatically checks for role expiry upon access validation. +- Provides functions to revoke temporal roles explicitly. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module extends standard access control by introducing time-bound role assignments. It allows for roles to be granted for a specific duration, automatically revoking them upon expiry. This enhances security and operational flexibility by enabling temporary permissions. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; + mapping(bytes32 role => bytes32 adminRole) adminRole; +}`} + + +--- +### AccessControlTemporalStorage + + +{`struct AccessControlTemporalStorage { + mapping(address account => mapping(bytes32 role => uint256 expiryTimestamp)) roleExpiry; +}`} + + +### State Variables + + + +## Functions + +### getAccessControlStorage + +Returns the storage for AccessControl. + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +**Returns:** + + + +--- +### getRoleExpiry + +function to get the expiry timestamp for a role assignment. + + +{`function getRoleExpiry(bytes32 _role, address _account) view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getStorage + +Returns the storage for AccessControlTemporal. + + +{`function getStorage() pure returns (AccessControlTemporalStorage storage s);`} + + +**Returns:** + + + +--- +### grantRoleWithExpiry + +function to grant a role with an expiry timestamp. + + +{`function grantRoleWithExpiry(bytes32 _role, address _account, uint256 _expiresAt) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isRoleExpired + +function to check if a role assignment has expired. + + +{`function isRoleExpired(bytes32 _role, address _account) view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### requireValidRole + +function to check if an account has a valid (non-expired) role. **Notes:** - Reverts with AccessControlUnauthorizedAccount If the account does not have the role. - Reverts with AccessControlRoleExpired If the role has expired. + + +{`function requireValidRole(bytes32 _role, address _account) view;`} + + +**Parameters:** + + + +--- +### revokeTemporalRole + +function to revoke a temporal role. + + +{`function revokeTemporalRole(bytes32 _role, address _account) returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Event emitted when a role is granted with an expiry timestamp. +
+ +
+ Signature: + +{`event RoleGrantedWithExpiry( +bytes32 indexed _role, address indexed _account, uint256 _expiresAt, address indexed _sender +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Event emitted when a temporal role is revoked. +
+ +
+ Signature: + +{`event TemporalRoleRevoked(bytes32 indexed _role, address indexed _account, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a role has expired. +
+ +
+ Signature: + +error AccessControlRoleExpired(bytes32 _role, address _account); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IAccessControlTemporalMod} from "@compose/contracts/modules/accessControl/IAccessControlTemporalMod.sol"; + +contract MyFacet { + IAccessControlTemporalMod internal accessControlTemporalMod; + + function initialize(address _accessControlTemporalModAddress) external { + accessControlTemporalMod = IAccessControlTemporalMod(_accessControlTemporalModAddress); + } + + function grantAdminRoleWithExpiry(address _account, uint64 _duration) external { + uint64 expiry = uint64(block.timestamp) + _duration; + accessControlTemporalMod.grantRoleWithExpiry(bytes32(0), _account, expiry); // Assuming role is 'DEFAULT_ADMIN_ROLE' + } + + function checkAdminRoleValidity(address _account) external view { + accessControlTemporalMod.requireValidRole(bytes32(0), _account); + } +}`} + + +## Best Practices + + +- Use `requireValidRole` before executing sensitive operations to ensure temporal roles are still active. +- Carefully manage role expiry durations to prevent accidental access loss or prolonged unauthorized access. +- Consider using custom errors for more granular revert reasons in your facet logic. + + +## Integration Notes + + +This module interacts with the diamond's storage to manage temporal role assignments. Facets calling `requireValidRole` will see the immediate effect of role grants or expirations. The module's storage should be initialized and accessible via the diamond proxy. Ensure the `AccessControlTemporalMod` facet is prioritized in the diamond's facet configuration if it relies on specific storage slots. + + +
+ +
+ + diff --git a/website/docs/library/access/AccessControlTemporal/_category_.json b/website/docs/library/access/AccessControlTemporal/_category_.json new file mode 100644 index 00000000..834b0b18 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Temporal Access Control", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/AccessControlTemporal/index" + } +} diff --git a/website/docs/library/access/AccessControlTemporal/index.mdx b/website/docs/library/access/AccessControlTemporal/index.mdx new file mode 100644 index 00000000..f8165020 --- /dev/null +++ b/website/docs/library/access/AccessControlTemporal/index.mdx @@ -0,0 +1,30 @@ +--- +title: "Temporal Access Control" +description: "Time-limited role-based access control." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Time-limited role-based access control. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/Owner/OwnerFacet.mdx b/website/docs/library/access/Owner/OwnerFacet.mdx new file mode 100644 index 00000000..31695bf3 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerFacet.mdx @@ -0,0 +1,197 @@ +--- +sidebar_position: 99 +title: "OwnerFacet" +description: "Manages contract ownership and transfer." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages contract ownership and transfer. + + + +- Defines and manages the owner of the diamond. +- Supports transferring ownership to a new address. +- Allows for renouncing ownership, making the diamond effectively immutable from an owner perspective. + + +## Overview + +The OwnerFacet provides essential ownership management capabilities for a Compose diamond. It allows an owner to transfer ownership to a new address or renounce ownership entirely, ensuring control and security over the diamond's administrative functions. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerFacet} from "../facets/owner/IOwnerFacet.sol"; + +contract OwnerFacetDeployer { + // Assuming the diamond proxy address is known + address immutable DIAMOND_PROXY_ADDRESS; + + constructor(address _diamondProxyAddress) { + DIAMOND_PROXY_ADDRESS = _diamondProxyAddress; + } + + function getOwner() public view returns (address) { + bytes4 selector = IOwnerFacet.owner.selector; + (bool success, bytes memory data) = DIAMOND_PROXY_ADDRESS.call(abi.encodeWithSelector(selector)); + require(success, "Failed to get owner"); + return abi.decode(data, (address)); + } + + function transferOwnership(address _newOwner) public { + bytes4 selector = IOwnerFacet.transferOwnership.selector; + (bool success, ) = DIAMOND_PROXY_ADDRESS.call(abi.encodeWithSelector(selector, _newOwner)); + require(success, "Failed to transfer ownership"); + } + + function renounceOwnership() public { + bytes4 selector = IOwnerFacet.renounceOwnership.selector; + (bool success, ) = DIAMOND_PROXY_ADDRESS.call(abi.encodeWithSelector(selector)); + require(success, "Failed to renounce ownership"); + } +}`} + + +## Best Practices + + +- Initialize ownership during diamond deployment, typically to the deployer address. +- Use `transferOwnership` to delegate administrative control to another address. +- Consider using `renounceOwnership` only when administrative control is no longer required and the diamond should be immutable. + + +## Security Considerations + + +Access to `transferOwnership` and `renounceOwnership` is restricted to the current owner. Ensure the owner's private key is secured to prevent unauthorized changes. Setting the new owner to `address(0)` is the mechanism for renouncing ownership; this action is irreversible. The `owner` function is public and can be called by any address. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/OwnerMod.mdx b/website/docs/library/access/Owner/OwnerMod.mdx new file mode 100644 index 00000000..fcc3ab74 --- /dev/null +++ b/website/docs/library/access/Owner/OwnerMod.mdx @@ -0,0 +1,253 @@ +--- +sidebar_position: 99 +title: "OwnerMod" +description: "Manages ERC-173 contract ownership." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/Owner/OwnerMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-173 contract ownership. + + + +- Provides standard ERC-173 ownership tracking. +- Includes `owner()` and `requireOwner()` for access control. +- Supports safe ownership transfer and renouncement. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides essential ERC-173 contract ownership functionality, enabling secure management and transfer of ownership. It defines storage for the owner and provides functions to retrieve, check, set, and transfer ownership, crucial for access control within a diamond. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { + address owner; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-173 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Get the address of the owner + + +{`function owner() view returns (address);`} + + +**Returns:** + + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### setContractOwner + + +{`function setContractOwner(address _initialOwner) ;`} + + +**Parameters:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract Set _newOwner to address(0) to renounce any ownership. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ This emits when ownership of a contract changes. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IOwnerMod} from "@compose/modules/owner/IOwnerMod.sol"; + +contract MyOwnerFacet { + address immutable DIAMOND_STORAGE_ADDRESS; + uint256 immutable OWNER_STORAGE_SLOT; + + constructor(address _diamondAddress, uint256 _ownerSlot) { + DIAMOND_STORAGE_ADDRESS = _diamondAddress; + OWNER_STORAGE_SLOT = _ownerSlot; + } + + function getOwner() external view returns (address) { + // Access OwnerMod storage directly using assembly + IOwnerMod.OwnerModStorage storage storagePtr = IOwnerMod.OwnerModStorage + (uint256(uint160(DIAMOND_STORAGE_ADDRESS)) + OWNER_STORAGE_SLOT); + return storagePtr.owner; + } + + function transferDiamondOwnership(address _newOwner) external { + // Call the internal OwnerMod function via the Diamond Proxy + IOwnerMod(DIAMOND_STORAGE_ADDRESS).transferOwnership(_newOwner); + } +}`} + + +## Best Practices + + +- Only the contract owner should call functions that modify ownership. +- Use `requireOwner()` to protect sensitive administrative functions. +- Be aware that renouncing ownership (setting owner to address(0)) is irreversible. + + +## Integration Notes + + +The OwnerMod utilizes a dedicated storage slot to store the `OwnerModStorage` struct, which contains the `owner` address. Facets interact with this storage either by directly accessing the slot via inline assembly (as demonstrated in `getStorage`) or by calling the module's functions through the diamond proxy. Changes to the owner are immediately reflected across all facets interacting with this module. + + +
+ +
+ + diff --git a/website/docs/library/access/Owner/_category_.json b/website/docs/library/access/Owner/_category_.json new file mode 100644 index 00000000..2ddf56c9 --- /dev/null +++ b/website/docs/library/access/Owner/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Owner", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/Owner/index" + } +} diff --git a/website/docs/library/access/Owner/index.mdx b/website/docs/library/access/Owner/index.mdx new file mode 100644 index 00000000..e619a378 --- /dev/null +++ b/website/docs/library/access/Owner/index.mdx @@ -0,0 +1,30 @@ +--- +title: "Owner" +description: "Single-owner access control pattern." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Single-owner access control pattern. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx new file mode 100644 index 00000000..14a2dc43 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsFacet.mdx @@ -0,0 +1,201 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsFacet" +description: "Owner Two Steps facet for Compose diamonds" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Owner Two Steps facet for Compose diamonds + + + +- Self-contained facet with no imports or inheritance +- Only `external` and `internal` function visibility +- Follows Compose readability-first conventions +- Ready for diamond integration + + +## Overview + +Owner Two Steps facet for Compose diamonds + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### owner + +Get the address of the owner + + +{`function owner() external view returns (address);`} + + +**Returns:** + + + +--- +### pendingOwner + +Get the address of the pending owner + + +{`function pendingOwner() external view returns (address);`} + + +**Returns:** + + + +--- +### transferOwnership + +Set the address of the new owner of the contract + + +{`function transferOwnership(address _newOwner) external;`} + + +**Parameters:** + + + +--- +### acceptOwnership + + +{`function acceptOwnership() external;`} + + +--- +### renounceOwnership + + +{`function renounceOwnership() external;`} + + +## Events + + + + +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ + +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx new file mode 100644 index 00000000..45a3aa6c --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/OwnerTwoStepsMod.mdx @@ -0,0 +1,302 @@ +--- +sidebar_position: 99 +title: "OwnerTwoStepsMod" +description: "Manages two-step contract ownership transfers securely." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/access/OwnerTwoSteps/OwnerTwoStepsMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages two-step contract ownership transfers securely. + + + +- Secure two-step ownership transfer to prevent accidental control loss. +- Explicit checks for owner and pending owner roles. +- Permissionless `renounceOwnership` option for relinquishing control. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module implements a secure two-step ownership transfer mechanism, preventing accidental loss of control. It ensures that ownership changes are explicitly confirmed by both the current and pending owners, enhancing the safety and auditability of diamond administration. + +--- + +## Storage + +### OwnerStorage + +storage-location: erc8042:compose.owner + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### PendingOwnerStorage + +storage-location: erc8042:compose.owner.pending + + +{`struct PendingOwnerStorage { + address pendingOwner; +}`} + + +### State Variables + + + +## Functions + +### acceptOwnership + +Finalizes ownership transfer; must be called by the pending owner. + + +{`function acceptOwnership() ;`} + + +--- +### getOwnerStorage + +Returns a pointer to the Owner storage struct. Uses inline assembly to access the storage slot defined by OWNER_STORAGE_POSITION. + + +{`function getOwnerStorage() pure returns (OwnerStorage storage s);`} + + +**Returns:** + + + +--- +### getPendingOwnerStorage + +Returns a pointer to the PendingOwner storage struct. Uses inline assembly to access the storage slot defined by PENDING_OWNER_STORAGE_POSITION. + + +{`function getPendingOwnerStorage() pure returns (PendingOwnerStorage storage s);`} + + +**Returns:** + + + +--- +### owner + +Returns the current owner. + + +{`function owner() view returns (address);`} + + +--- +### pendingOwner + +Returns the pending owner (if any). + + +{`function pendingOwner() view returns (address);`} + + +--- +### renounceOwnership + +Renounce ownership of the contract Sets the owner to address(0), disabling all functions restricted to the owner. + + +{`function renounceOwnership() ;`} + + +--- +### requireOwner + +Reverts if the caller is not the owner. + + +{`function requireOwner() view;`} + + +--- +### transferOwnership + +Initiates a two-step ownership transfer. + + +{`function transferOwnership(address _newOwner) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership transfer is initiated (pending owner set). +
+ +
+ Signature: + +{`event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+ +
+ Emitted when ownership transfer is finalized. +
+ +
+ Signature: + +{`event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerAlreadyRenounced(); + +
+
+ + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {OwnerTwoStepsMod} from "@compose-protocol/diamond-contracts/modules/ownership/OwnerTwoStepsMod.sol"; + +contract MyFacet is OwnerTwoStepsMod { + // Define storage slots for owner and pending owner + uint256 constant OWNER_STORAGE_POSITION = 1; + uint256 constant PENDING_OWNER_STORAGE_POSITION = 2; + + // Function to get the owner + function getOwner() public view returns (address) { + return owner(); + } + + // Function to initiate ownership transfer + function initiateTransfer(address _newOwner) external { + transferOwnership(_newOwner); + } + + // Function to accept ownership + function acceptOwnershipTransfer() external { + acceptOwnership(); + } + + // Function to renounce ownership + function renounceContractOwnership() external { + renounceOwnership(); + } +}`} + + +## Best Practices + + +- Always use `transferOwnership` to initiate a transfer, followed by `acceptOwnership` from the new owner. +- Implement `requireOwner` checks judiciously to protect critical administrative functions. +- Consider the implications of `renounceOwnership` as it renders all owner-restricted functions unusable. + + +## Integration Notes + + +This module utilizes specific storage slots for `Owner` and `PendingOwner`. Facets interacting with this module should be aware of these storage positions to correctly initialize and access ownership data. Ensure that the `OWNER_STORAGE_POSITION` and `PENDING_OWNER_STORAGE_POSITION` are defined and unique within your diamond's storage layout. The module relies on inline assembly to access these storage slots. + + +
+ +
+ + diff --git a/website/docs/library/access/OwnerTwoSteps/_category_.json b/website/docs/library/access/OwnerTwoSteps/_category_.json new file mode 100644 index 00000000..90b66a92 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Two-Step Owner", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/OwnerTwoSteps/index" + } +} diff --git a/website/docs/library/access/OwnerTwoSteps/index.mdx b/website/docs/library/access/OwnerTwoSteps/index.mdx new file mode 100644 index 00000000..bfade888 --- /dev/null +++ b/website/docs/library/access/OwnerTwoSteps/index.mdx @@ -0,0 +1,30 @@ +--- +title: "Two-Step Owner" +description: "Two-step ownership transfer pattern." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Two-step ownership transfer pattern. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/access/_category_.json b/website/docs/library/access/_category_.json new file mode 100644 index 00000000..cbc9d5ba --- /dev/null +++ b/website/docs/library/access/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Access Control", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/access/index" + } +} diff --git a/website/docs/library/access/index.mdx b/website/docs/library/access/index.mdx new file mode 100644 index 00000000..edf619c1 --- /dev/null +++ b/website/docs/library/access/index.mdx @@ -0,0 +1,51 @@ +--- +title: "Access Control" +description: "Access control patterns for permission management in Compose diamonds." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Access control patterns for permission management in Compose diamonds. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/diamond/DiamondCutFacet.mdx b/website/docs/library/diamond/DiamondCutFacet.mdx new file mode 100644 index 00000000..87b8a1c9 --- /dev/null +++ b/website/docs/library/diamond/DiamondCutFacet.mdx @@ -0,0 +1,308 @@ +--- +sidebar_position: 99 +title: "DiamondCutFacet" +description: "Manage diamond facets and functions" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and functions + + + +- Add, replace, and remove functions atomically using selectors. +- Supports executing an initialization function during a diamond cut. +- Manages function mappings and facet addresses within the diamond proxy. + + +## Overview + +The DiamondCutFacet enables programmatic management of a diamond's facets and functions. It allows adding, replacing, and removing functions, as well as executing an initialization function during a cut. This facet is crucial for upgrading and extending the diamond's functionality. + +--- + +## Storage + +### OwnerStorage + + +{`struct OwnerStorage { + address owner; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error OwnerUnauthorizedAccount(); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose-protocol/diamond/contracts/facets/IDiamondCut.sol"; + +contract DiamondManager { + address immutable DIAMOND_ADDRESS; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function upgradeDiamond() external { + bytes32[] memory selectors = new bytes32[](1); + selectors[0] = IDiamondCut.addFunctions.selector; + + address[] memory facetAddresses = new address[](1); + facetAddresses[0] = address(this); // Assuming this contract deploys the new facet + + IDiamondCut(DIAMOND_ADDRESS).diamondCut(facetCuts, address(0), ""); // Placeholder for actual cuts + } + + // ... other deployment and upgrade logic ... +}`} + + +## Best Practices + + +- Use `diamondCut` with extreme care, as it modifies the diamond's core functionality. +- Ensure new facets are properly initialized if required, and the initialization function is correctly specified. +- Store the `DIAMOND_ADDRESS` in your deployment scripts or contracts to interact with the `DiamondCutFacet`. + + +## Security Considerations + + +Access to `diamondCut` should be restricted to authorized addresses (e.g., the diamond's owner) to prevent unauthorized modifications. Incorrectly specified selectors or facet addresses can lead to broken functionality or loss of access. Initialization functions must be carefully audited to prevent reentrancy or state corruption. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondCutMod.mdx b/website/docs/library/diamond/DiamondCutMod.mdx new file mode 100644 index 00000000..3a19fa17 --- /dev/null +++ b/website/docs/library/diamond/DiamondCutMod.mdx @@ -0,0 +1,393 @@ +--- +sidebar_position: 99 +title: "DiamondCutMod" +description: "Manage diamond facets and function pointers." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondCutMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage diamond facets and function pointers. + + + +- Supports adding, replacing, and removing functions from a diamond. +- Allows execution of an initialization function during a diamond cut. +- Provides granular control over facet management through selectors. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondCut module provides essential functions for managing the facets of a Compose diamond. It allows for the addition, replacement, and removal of functions, enabling dynamic upgrades and modifications to the diamond's capabilities. This module is crucial for maintaining and evolving the diamond's logic in a composable and upgrade-safe manner. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond + */ + bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFunctions + + +{`function addFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### diamondCut + +Add/replace/remove any number of functions and optionally execute a function with delegatecall + + +{`function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) ;`} + + +**Parameters:** + + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +--- +### removeFunctions + + +{`function removeFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +--- +### replaceFunctions + + +{`function replaceFunctions(address _facet, bytes4[] calldata _functionSelectors) ;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotRemoveImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); + +
+
+ + +
+ Signature: + +error CannotReplaceImmutableFunction(bytes4 _selector); + +
+
+ + +
+ Signature: + +error IncorrectFacetCutAction(uint8 _action); + +
+
+ + +
+ Signature: + +error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+ + +
+ Signature: + +error NoSelectorsProvidedForFacet(address _facet); + +
+
+ + +
+ Signature: + +error RemoveFacetAddressMustBeZeroAddress(address _facet); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond-proxy/contracts/modules/diamondCut/IDiamondCut.sol"; +import {Selectors} from "@compose/diamond-proxy/contracts/utils/Selectors.sol"; + +contract MyFacet { + // Facet implementation details... +} + +contract DiamondDeployer { + IDiamondCut public diamondCutFacet; + + function upgradeDiamond(address _diamondAddress) external { + // Assuming diamondCutFacet is already deployed and accessible + diamondCutFacet = IDiamondCut(_diamondAddress); + + // Example: Add a new function + address newFacetAddress = address(new MyFacet()); + bytes4[] memory selectorsToAdd = new bytes4[](1); + selectorsToAdd[0] = MyFacet.myNewFunction.selector; + + // Example: Remove a function + bytes4[] memory selectorsToRemove = new bytes4[](1); + selectorsToRemove[0] = MyFacet.oldFunction.selector; + + // Execute the diamond cut + diamondCutFacet.diamondCut( + IDiamondCut.FacetCut[](\(IDiamondCut.FacetCut[]) + (new IDiamondCut.FacetCut[](2)) + ), + newFacetAddress, // Target address for adding/replacing functions + bytes("") // Initialization calldata + ); + + // Note: For a real upgrade, you would construct the FacetCut array correctly + // with actions (ADD, REPLACE, REMOVE), facet addresses, and selectors. + } +}`} + + +## Best Practices + + +- Use custom errors for revert conditions to enhance clarity and gas efficiency. +- Ensure facet addresses used in `diamondCut` are valid and the associated bytecode is accessible. +- Carefully manage facet additions and removals to avoid breaking existing diamond functionality; consider immutability constraints. + + +## Integration Notes + + +The DiamondCut module directly interacts with the diamond's storage to manage the mapping of selectors to facet addresses. Any changes made through `diamondCut` are immediately reflected in the diamond proxy's dispatch logic. Facets added or replaced become callable via the diamond proxy. Facets removed become inaccessible. The order of operations within a single `diamondCut` call is important for preventing accidental removal of functions that are about to be added or replaced from the same address. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondInspectFacet.mdx b/website/docs/library/diamond/DiamondInspectFacet.mdx new file mode 100644 index 00000000..b0501b48 --- /dev/null +++ b/website/docs/library/diamond/DiamondInspectFacet.mdx @@ -0,0 +1,156 @@ +--- +sidebar_position: 99 +title: "DiamondInspectFacet" +description: "Inspects diamond storage and function mappings." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondInspectFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Inspects diamond storage and function mappings. + + + +- Provides direct access to the diamond's storage slot via inline assembly. +- Exposes a mapping of all registered function selectors to their respective facet addresses. +- Enables off-chain tooling and on-chain contract logic to understand diamond composition. + + +## Overview + +The DiamondInspectFacet provides read-only access to the diamond's internal state and function routing information. It allows developers to query the raw storage layout and map function selectors to their implementing facets, aiding in debugging and understanding the diamond's composition. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### FunctionFacetPair + + +{`struct FunctionFacetPair { + bytes4 selector; + address facet; +}`} + + +### State Variables + + + +## Functions + +### functionFacetPairs + +Returns an array of all function selectors and their corresponding facet addresses. Iterates through the diamond's stored selectors and pairs each with its facet. + + +{`function functionFacetPairs() external view returns (FunctionFacetPair[] memory pairs);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondInspectFacet} from "@compose/contracts/facets/DiamondInspect/IDiamondInspectFacet.sol"; + +contract DiamondInspectUser { + address immutable DIAMOND_PROXY; + + constructor(address diamondProxyAddress) { + DIAMOND_PROXY = diamondProxyAddress; + } + + function inspectDiamond() external view returns (bytes[] memory, (address, address)[]) { + IDiamondInspectFacet diamondInspect = IDiamondInspectFacet(DIAMOND_PROXY); + + // Retrieve the raw storage layout (requires custom ABI encoding or knowledge of storage structure) + // For demonstration, assume a method to decode storage is available externally or via another facet. + // bytes[] memory storageLayout = diamondInspect.getStorage(); // Note: getStorage returns raw bytes, decoding is external. + + // Retrieve function selector to facet address mappings + (address, address)[] memory functionFacetPairs = diamondInspect.functionFacetPairs(); + + return (new bytes[](0), functionFacetPairs); // Placeholder for storageLayout + } +}`} + + +## Best Practices + + +- Integrate this facet to facilitate on-chain inspection of diamond state and function routing. +- Use `functionFacetPairs` to verify function ownership and routing during upgrades or debugging. +- Be aware that `getStorage` returns raw bytes; interpretation requires knowledge of the diamond's storage layout. + + +## Security Considerations + + +This facet is read-only and does not modify state. However, exposing raw storage could reveal sensitive internal data if not properly managed by other facets. Ensure that sensitive data is not stored in an easily accessible manner if this facet is exposed. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondLoupeFacet.mdx b/website/docs/library/diamond/DiamondLoupeFacet.mdx new file mode 100644 index 00000000..08a08f8d --- /dev/null +++ b/website/docs/library/diamond/DiamondLoupeFacet.mdx @@ -0,0 +1,247 @@ +--- +sidebar_position: 99 +title: "DiamondLoupeFacet" +description: "Inspect diamond facets, addresses, and selectors." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondLoupeFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Inspect diamond facets, addresses, and selectors. + + + +- Provides read-only access to diamond's facet registry. +- Optimized for gas efficiency, especially with a large number of facets and selectors. +- Enables dynamic discovery of diamond functionality. + + +## Overview + +The DiamondLoupeFacet provides essential introspection capabilities for a Compose diamond. It allows developers to query which facets are registered, their associated addresses, and the function selectors each facet implements. This is crucial for understanding diamond composition, debugging, and dynamic interaction. + +--- + +## Storage + +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### DiamondStorage + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * Array of all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### Facet + + +{`struct Facet { + address facet; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### facetAddress + +Gets the facet address that supports the given selector. If facet is not found return address(0). + + +{`function facetAddress(bytes4 _functionSelector) external view returns (address facet);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetFunctionSelectors + +Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to `_facet`. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value. + + +{`function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### facetAddresses + +Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios. + + +{`function facetAddresses() external view returns (address[] memory allFacets);`} + + +**Returns:** + + + +--- +### facets + +Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive. + + +{`function facets() external view returns (Facet[] memory facetsAndSelectors);`} + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondLoupe} from "@compose-protocol/diamond-interface/src/facets/IDiamondLoupe.sol"; + +contract DiamondLoupeConsumer { + IDiamondLoupe diamondLoupeFacet; + + constructor(address _diamondAddress) { + diamondLoupeFacet = IDiamondLoupe(_diamondAddress); + } + + function getFacetAddresses() public view returns (address[] memory) { + return diamondLoupeFacet.facetAddresses(); + } + + function getFacetSelectors(address _facet) public view returns (bytes4[] memory) { + return diamondLoupeFacet.facetFunctionSelectors(_facet); + } + + function getAllFacets() public view returns (IDiamondLoupe.Facet[] memory) { + return diamondLoupeFacet.facets(); + } +}`} + + +## Best Practices + + +- Use `facetAddresses()` to retrieve all unique facet addresses registered with the diamond. +- Employ `facetFunctionSelectors(address)` to determine the specific functions implemented by a given facet. +- Utilize `facets()` for a comprehensive view of all registered facets and their corresponding selectors. + + +## Security Considerations + + +This facet is read-only and does not manage state changes, thus posing minimal direct security risks. Ensure the diamond proxy itself is properly secured to prevent unauthorized facet additions or removals. + + +
+ +
+ + diff --git a/website/docs/library/diamond/DiamondMod.mdx b/website/docs/library/diamond/DiamondMod.mdx new file mode 100644 index 00000000..5f5a0dce --- /dev/null +++ b/website/docs/library/diamond/DiamondMod.mdx @@ -0,0 +1,233 @@ +--- +sidebar_position: 99 +title: "DiamondMod" +description: "Manages diamond facets and their function mappings." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/DiamondMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages diamond facets and their function mappings. + + + +- Supports adding multiple facets and their function selectors in a single transaction during deployment. +- Provides the `diamondFallback` mechanism to route calls to the appropriate facet. +- Exposes `getStorage` for introspection of diamond storage layout. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The DiamondMod provides core functionality for managing facets within a Compose diamond. It handles the addition of new facets and their associated function selectors during initial deployment. This module is crucial for composing diamond functionality by mapping external calls to the correct facet implementation. + +--- + +## Storage + +### FacetCutAction + +Add=0, Replace=1, Remove=2 + +--- +### DiamondStorage + +storage-location: erc8042:compose.diamond + + +{`struct DiamondStorage { + mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition; + /** + * \`selectors\` contains all function selectors that can be called in the diamond. + */ + bytes4[] selectors; +}`} + + +--- +### FacetAndPosition + + +{`struct FacetAndPosition { + address facet; + uint32 position; +}`} + + +--- +### FacetCut + + +{`struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; +}`} + + +### State Variables + + + +## Functions + +### addFacets + +Adds facets and their function selectors to the diamond. Only supports adding functions during diamond deployment. + + +{`function addFacets(FacetCut[] memory _facets) ;`} + + +**Parameters:** + + + +--- +### diamondFallback + +Find facet for function that is called and execute the function if a facet is found and return any value. + + +{`function diamondFallback() ;`} + + +--- +### getStorage + + +{`function getStorage() pure returns (DiamondStorage storage s);`} + + +## Events + + + + +
+ Signature: + +{`event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); + +
+
+ + +
+ Signature: + +error FunctionNotFound(bytes4 _selector); + +
+
+ + +
+ Signature: + +error InvalidActionWhenDeployingDiamond(address facetAddress, FacetCutAction action, bytes4[] functionSelectors); + +
+
+ + +
+ Signature: + +error NoBytecodeAtAddress(address _contractAddress, string _message); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondMod} from "@compose/diamond-proxy/contracts/modules/DiamondMod.sol"; + +contract MyFacet { + // Facet implementation details... +} + +contract Deployer { + // Assuming diamondMod is an instance of DiamondMod + IDiamondMod internal diamondMod; + + function deployDiamond(bytes[] memory facetBytecodes, bytes32[] memory functionSigs) external { + // ... deployment logic ... + // diamondMod.addFacets(facetBytecodes, functionSigs); // Example call during deployment + } +}`} + + +## Best Practices + + +- Facet additions are restricted to the initial diamond deployment phase to maintain state immutability post-deployment. +- Ensure function selectors and their corresponding facet bytecodes are correctly paired to prevent runtime errors. +- Utilize custom errors for clear and gas-efficient error handling during facet management operations. + + +## Integration Notes + + +The DiamondMod interacts directly with the diamond's storage to register facet implementations and their function selectors. The `addFacets` function is designed to be called only during the initial deployment of the diamond proxy contract. Subsequent modifications to facets or function mappings are not supported by this module post-deployment, enforcing diamond immutability. The `diamondFallback` function relies on the mappings established by `addFacets` to route incoming calls. + + +
+ +
+ + diff --git a/website/docs/library/diamond/_category_.json b/website/docs/library/diamond/_category_.json new file mode 100644 index 00000000..26c8cc37 --- /dev/null +++ b/website/docs/library/diamond/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Diamond Core", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/diamond/index" + } +} diff --git a/website/docs/library/diamond/example/ExampleDiamond.mdx b/website/docs/library/diamond/example/ExampleDiamond.mdx new file mode 100644 index 00000000..ef59906f --- /dev/null +++ b/website/docs/library/diamond/example/ExampleDiamond.mdx @@ -0,0 +1,139 @@ +--- +sidebar_position: 99 +title: "ExampleDiamond" +description: "Example Diamond for Compose framework" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/diamond/example/ExampleDiamond.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Example Diamond for Compose framework + + + +- Demonstrates diamond proxy initialization. +- Supports adding and registering facets with their selectors. +- Establishes contract ownership for management. + + +## Overview + +The ExampleDiamond contract serves as a foundational template within the Compose framework. It demonstrates the core diamond proxy pattern, enabling the registration and routing of functions across various facets. This contract is essential for understanding how to initialize and manage facets within a diamond. + +--- + +## Storage + +## Functions + +### constructor + +Struct to hold facet address and its function selectors. struct FacetCut { address facetAddress; FacetCutAction action; // Add=0, Replace=1, Remove=2 bytes4[] functionSelectors; } Initializes the diamond contract with facets, owner and other data. Adds all provided facets to the diamond's function selector mapping and sets the contract owner. Each facet in the array will have its function selectors registered to enable delegatecall routing. + + +{`constructor(DiamondMod.FacetCut[] memory _facets, address _diamondOwner) ;`} + + +**Parameters:** + + + +--- +### fallback + + +{`fallback() external payable;`} + + +--- +### receive + + +{`receive() external payable;`} + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {DiamondCutFacet} from "@compose/diamond/facets/DiamondCutFacet.sol"; +import {DiamondLoupeFacet} from "@compose/diamond/facets/DiamondLoupeFacet.sol"; +import {ExampleDiamond} from "@compose/diamond/ExampleDiamond.sol"; + +contract DeployExampleDiamond is DiamondCutFacet { + address public diamondProxy; + + function deploy() external { + // Initialize facets array + FacetCut[] memory facets = new FacetCut[](2); + + // Add DiamondCutFacet + facets[0] = FacetCut({ + facetAddress: address(new DiamondCutFacet()), + action: FacetCutAction.Add, + functionSelectors: DiamondCutFacet.getFunctionSelectors() + }); + + // Add DiamondLoupeFacet + facets[1] = FacetCut({ + facetAddress: address(new DiamondLoupeFacet()), + action: FacetCutAction.Add, + functionSelectors: DiamondLoupeFacet.getFunctionSelectors() + }); + + // Deploy ExampleDiamond and initialize it + ExampleDiamond exampleDiamond = new ExampleDiamond(); + exampleDiamond.init(facets, msg.sender); + diamondProxy = address(exampleDiamond); + } +}`} + + +## Best Practices + + +- Initialize the diamond with essential facets like DiamondCutFacet and DiamondLoupeFacet. +- Ensure the owner is set correctly during initialization to manage future upgrades. +- Register function selectors accurately for each facet to enable proper routing. + + +## Security Considerations + + +The constructor initializes the diamond and sets the owner. Ensure the owner address is a trusted entity. Function selectors must be correctly mapped to facet addresses to prevent unexpected behavior or denial of service. + + +
+ +
+ + diff --git a/website/docs/library/diamond/example/_category_.json b/website/docs/library/diamond/example/_category_.json new file mode 100644 index 00000000..8e4d0ed5 --- /dev/null +++ b/website/docs/library/diamond/example/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "example", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/diamond/example/index" + } +} diff --git a/website/docs/library/diamond/example/index.mdx b/website/docs/library/diamond/example/index.mdx new file mode 100644 index 00000000..4513c4b1 --- /dev/null +++ b/website/docs/library/diamond/example/index.mdx @@ -0,0 +1,23 @@ +--- +title: "example" +description: "example components for Compose diamonds." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + example components for Compose diamonds. + + + + } + size="medium" + /> + diff --git a/website/docs/library/diamond/index.mdx b/website/docs/library/diamond/index.mdx new file mode 100644 index 00000000..7285988b --- /dev/null +++ b/website/docs/library/diamond/index.mdx @@ -0,0 +1,58 @@ +--- +title: "Diamond Core" +description: "Core diamond proxy functionality for ERC-2535 diamonds." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Core diamond proxy functionality for ERC-2535 diamonds. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/index.mdx b/website/docs/library/index.mdx new file mode 100644 index 00000000..8de81297 --- /dev/null +++ b/website/docs/library/index.mdx @@ -0,0 +1,51 @@ +--- +title: "Library" +description: "API reference for all Compose modules and facets." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + API reference for all Compose modules and facets. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx new file mode 100644 index 00000000..6a6d54f9 --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Facet.mdx @@ -0,0 +1,152 @@ +--- +sidebar_position: 99 +title: "ERC165Facet" +description: "Implements ERC-165 interface detection for diamond contracts." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements ERC-165 interface detection for diamond contracts. + + + +- Implements the `supportsInterface` function as required by ERC-165. +- Utilizes inline assembly for efficient access to ERC-165 storage. + + +## Overview + +The ERC165Facet enables diamond contracts to comply with the ERC-165 standard, allowing external contracts to query which interfaces the diamond supports. This facet is crucial for discoverability and interoperability within the Ethereum ecosystem. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { + /** + * @notice Mapping of interface IDs to whether they are supported + */ + mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### supportsInterface + +Query if a contract implements an interface This function checks if the diamond supports the given interface ID + + +{`function supportsInterface(bytes4 _interfaceId) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC165Facet} from "@compose/contracts/src/facets/ERC165Facet.sol"; +import {IDiamondCut} from "@compose/contracts/src/interfaces/IDiamondCut.sol"; + +contract MyDiamond is IDiamondCut { + // ... other facet deployments and cuts ... + + function upgrade() external payable { + // ... other facet cuts ... + + address[] memory facetsToAddAddresses = new address[](1); + bytes[] memory facetToAddABIs = new bytes[](1); + + facetsToAddAddresses[0] = address(new ERC165Facet()); + facetToAddABIs[0] = abi.encodeCall(ERC165Facet.supportsInterface, (bytes4(keccak256("supportsInterface(bytes4)")))); + + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](1); + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: facetsToAddAddresses[0], + action: IDiamondCut.FacetCutAction.ADD, + isConstructor: false, + functionSelectors: facetToAddABIs[0] + }); + + diamondCut(cuts, address(0), ""); + } + + // ... other diamond functions ... +}`} + + +## Best Practices + + +- Ensure the ERC165Facet is added to the diamond during initial deployment or upgrades. +- Correctly populate the diamond's storage with supported interface IDs to be queried by `supportsInterface`. + + +## Security Considerations + + +This facet is read-only and does not directly handle asset transfers or critical state changes, minimizing reentrancy risks. Ensure that the interface IDs registered in the diamond's storage accurately reflect the implemented functionalities. + + +
+ +
+ + diff --git a/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx new file mode 100644 index 00000000..54a7fdea --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/ERC165Mod.mdx @@ -0,0 +1,154 @@ +--- +sidebar_position: 99 +title: "ERC165Mod" +description: "ERC-165 interface detection and registration." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/interfaceDetection/ERC165/ERC165Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-165 interface detection and registration. + + + +- Implements ERC-165 standard for interface detection. +- Allows registration of supported interfaces at the facet level. +- Minimal storage footprint, designed for composition within the diamond storage pattern. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC165Mod provides the necessary storage and logic for implementing the ERC-165 standard on a diamond proxy. It allows facets to register supported interfaces, enabling external contracts to query the diamond's capabilities through the `supportsInterface` function. + +--- + +## Storage + +### ERC165Storage + + +{`struct ERC165Storage { + /* + * @notice Mapping of interface IDs to whether they are supported + */ + mapping(bytes4 => bool) supportedInterfaces; +}`} + + +### State Variables + + + +## Functions + +### getStorage + +Returns a pointer to the ERC-165 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC165Storage storage s);`} + + +**Returns:** + + + +--- +### registerInterface + +Register that a contract supports an interface Call this function during initialization to register supported interfaces. For example, in an ERC721 facet initialization, you would call: `LibERC165.registerInterface(type(IERC721).interfaceId)` + + +{`function registerInterface(bytes4 _interfaceId) ;`} + + +**Parameters:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC165Mod, IERC165Mod} from "@compose-protocol/diamond-contracts/contracts/modules/ERC165Mod.sol"; + +contract MyERC721Facet { + struct MyFacetStorage { + ERC165Mod.Storage erc165Storage; + // other facet storage... + } + + function initialize(MyFacetStorage storage self) external { + // Register ERC721 interface + ERC165Mod.registerInterface(self.erc165Storage, type(IERC721).interfaceId); + } + + // Other ERC721 functions... +}`} + + +## Best Practices + + +- Call `registerInterface` during facet initialization to ensure supported interfaces are declared before any external calls are made. +- Ensure the `ERC165Mod.Storage` struct is correctly laid out in your facet's storage and is initialized. +- Rely on the diamond's `supportsInterface` function, which aggregates results from all registered facets. + + +## Integration Notes + + +The ERC165Mod utilizes a dedicated storage slot for its `Storage` struct. Facets that implement ERC-165 functionality must include this struct in their own storage layout and call `ERC165Mod.registerInterface` during their initialization. The diamond's `supportsInterface` function aggregates the interface IDs registered by all facets to provide a comprehensive answer. + + +
+ +
+ + diff --git a/website/docs/library/interfaceDetection/ERC165/_category_.json b/website/docs/library/interfaceDetection/ERC165/_category_.json new file mode 100644 index 00000000..2396f18a --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-165", + "position": 99, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/interfaceDetection/ERC165/index" + } +} diff --git a/website/docs/library/interfaceDetection/ERC165/index.mdx b/website/docs/library/interfaceDetection/ERC165/index.mdx new file mode 100644 index 00000000..8de71f9d --- /dev/null +++ b/website/docs/library/interfaceDetection/ERC165/index.mdx @@ -0,0 +1,30 @@ +--- +title: "ERC-165" +description: "ERC-165 components for Compose diamonds." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-165 components for Compose diamonds. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/interfaceDetection/_category_.json b/website/docs/library/interfaceDetection/_category_.json new file mode 100644 index 00000000..a184d836 --- /dev/null +++ b/website/docs/library/interfaceDetection/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Interface Detection", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/interfaceDetection/index" + } +} diff --git a/website/docs/library/interfaceDetection/index.mdx b/website/docs/library/interfaceDetection/index.mdx new file mode 100644 index 00000000..17feecdd --- /dev/null +++ b/website/docs/library/interfaceDetection/index.mdx @@ -0,0 +1,23 @@ +--- +title: "Interface Detection" +description: "ERC-165 interface detection support." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-165 interface detection support. + + + + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC1155/ERC1155Facet.mdx b/website/docs/library/token/ERC1155/ERC1155Facet.mdx new file mode 100644 index 00000000..ccfdce70 --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Facet.mdx @@ -0,0 +1,653 @@ +--- +sidebar_position: 99 +title: "ERC1155Facet" +description: "Implements the ERC-1155 multi-token standard." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements the ERC-1155 multi-token standard. + + + +- Implements ERC-1155 standard functions: `balanceOf`, `balanceOfBatch`, `uri`, `safeTransferFrom`, `safeBatchTransferFrom`, `setApprovalForAll`, `isApprovedForAll`. +- Supports token-specific URIs in addition to a base URI. +- Utilizes inline assembly for efficient access to diamond storage. + + +## Overview + +This facet provides a robust implementation of the ERC-1155 multi-token standard, enabling the management and transfer of fungible and non-fungible tokens within a Compose diamond. It handles token balances, approvals, and URI retrieval, adhering to the standard's specifications for composability. + +--- + +## Storage + +### ERC1155Storage + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### uri + +Returns the URI for token type `_id`. If a token-specific URI is set in tokenURIs[_id], returns the concatenation of baseURI and tokenURIs[_id]. Note that baseURI is empty by default and must be set explicitly if concatenation is desired. If no token-specific URI is set, returns the default URI which applies to all token types. The default URI may contain the substring `{id}` which clients should replace with the actual token ID. + + +{`function uri(uint256 _id) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the amount of tokens of token type `id` owned by `account`. + + +{`function balanceOf(address _account, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOfBatch + +Batched version of balanceOf. + + +{`function balanceOfBatch(address[] calldata _accounts, uint256[] calldata _ids) + external + view + returns (uint256[] memory balances);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setApprovalForAll + +Grants or revokes permission to `operator` to transfer the caller's tokens. Emits an ApprovalForAll event. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### isApprovedForAll + +Returns true if `operator` is approved to transfer `account`'s tokens. + + +{`function isApprovedForAll(address _account, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### safeTransferFrom + +Transfers `value` amount of token type `id` from `from` to `to`. Emits a TransferSingle event. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Batched version of safeTransferFrom. Emits a TransferBatch event. + + +{`function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. +
+ +
+ Signature: + +{`event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Equivalent to multiple TransferSingle events, where `operator`, `from` and `to` are the same for all transfers. +
+ +
+ Signature: + +{`event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when `account` grants or revokes permission to `operator` to transfer their tokens. +
+ +
+ Signature: + +{`event ApprovalForAll(address indexed _account, address indexed _operator, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `id` changes to `value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Error indicating insufficient balance for a transfer. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Error indicating the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Error indicating the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Error indicating missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+ +
+ Error indicating the approver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidApprover(address _approver); + +
+
+ +
+ Error indicating the operator address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidOperator(address _operator); + +
+
+ +
+ Error indicating array length mismatch in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; +import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; + +// Assume Diamond ABI and implementation contracts are available +// For example, ERC1155Facet contract is deployed and its ABI is known + +contract DeployERC1155 { + address public diamondAddress; + + function deploy(address _diamondAddress) external { + diamondAddress = _diamondAddress; + } + + function getERC1155Facet() internal view returns (IERC1155) { + // Selector for ERC1155Facet.safeTransferFrom + bytes4 selector = bytes4(keccak256("safeTransferFrom(address,address,uint256,uint256,bytes)")); + return IERC1155(address(uint160(address(diamondAddress)) - selector)); + } + + function transferToken(address _from, address _to, uint256 _id, uint256 _value) external { + IERC1155 erc1155Facet = getERC1155Facet(); + erc1155Facet.safeTransferFrom(_from, _to, _id, _value, ""); + } + + function getBalance(address _account, uint256 _id) external view returns (uint256) { + IERC1155 erc1155Facet = getERC1155Facet(); + return erc1155Facet.balanceOf(_account, _id); + } +}`} + + +## Best Practices + + +- Initialize the `baseURI` and `tokenURIs` storage variables via an initializer function or a separate facet if needed. +- Use `safeTransferFrom` and `safeBatchTransferFrom` for all token transfers to ensure proper checks and event emissions. +- Manage approvals using `setApprovalForAll` before allowing operators to transfer tokens on behalf of an account. + + +## Security Considerations + + +Ensure that the `safeTransferFrom` and `safeBatchTransferFrom` functions are called with valid `from`, `to`, `id`, and `value` parameters to prevent unexpected behavior. The `ERC1155MissingApprovalForAll` and `ERC1155InvalidOperator` errors are crucial for preventing unauthorized transfers. Reentrancy is mitigated as transfers are internal and do not involve external calls to untrusted contracts within the transfer logic itself. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/ERC1155Mod.mdx b/website/docs/library/token/ERC1155/ERC1155Mod.mdx new file mode 100644 index 00000000..8d6f78bb --- /dev/null +++ b/website/docs/library/token/ERC1155/ERC1155Mod.mdx @@ -0,0 +1,601 @@ +--- +sidebar_position: 99 +title: "ERC1155Mod" +description: "Manages ERC-1155 token transfers, minting, and burning." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC1155/ERC1155Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-1155 token transfers, minting, and burning. + + + +- Supports both single and batch operations for transfers, minting, and burning. +- Includes `safeTransferFrom` and `safeBatchTransferFrom` for secure transfers to ERC-1155 compliant receivers. +- Provides functions to set and retrieve token URIs, enabling metadata management. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC1155Mod provides comprehensive functionality for managing ERC-1155 tokens within a Compose diamond. It enables minting, burning, and safe transfers of both single and batch token types, adhering to EIP-1155 standards. This module ensures proper handling of token balances and receiver interactions, crucial for composable NFT and fungible token systems. + +--- + +## Storage + +### ERC1155Storage + +ERC-8042 compliant storage struct for ERC-1155 token data. storage-location: erc8042:compose.erc1155 + + +{`struct ERC1155Storage { + mapping(uint256 id => mapping(address account => uint256 balance)) balanceOf; + mapping(address account => mapping(address operator => bool)) isApprovedForAll; + string uri; + string baseURI; + mapping(uint256 tokenId => string) tokenURIs; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns a single token type from an address. Decreases the balance and emits a TransferSingle event. Reverts if the account has insufficient balance. + + +{`function burn(address _from, uint256 _id, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burnBatch + +Burns multiple token types from an address in a single transaction. Decreases balances for each token type and emits a TransferBatch event. Reverts if the account has insufficient balance for any token type. + + +{`function burnBatch(address _from, uint256[] memory _ids, uint256[] memory _values) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-1155 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getStorage() pure returns (ERC1155Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a single token type to an address. Increases the balance and emits a TransferSingle event. Performs receiver validation if recipient is a contract. + + +{`function mint(address _to, uint256 _id, uint256 _value, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### mintBatch + +Mints multiple token types to an address in a single transaction. Increases balances for each token type and emits a TransferBatch event. Performs receiver validation if recipient is a contract. + + +{`function mintBatch(address _to, uint256[] memory _ids, uint256[] memory _values, bytes memory _data) ;`} + + +**Parameters:** + + + +--- +### safeBatchTransferFrom + +Safely transfers multiple token types from one address to another in a single transaction. Validates ownership, approval, and receiver address before updating balances for each token type. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeBatchTransferFrom( +address _from, +address _to, +uint256[] memory _ids, +uint256[] memory _values, +address _operator +) ;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a single token type from one address to another. Validates ownership, approval, and receiver address before updating balances. Performs ERC1155Receiver validation if recipient is a contract (safe transfer). Complies with EIP-1155 safe transfer requirements. + + +{`function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, address _operator) ;`} + + +**Parameters:** + + + +--- +### setBaseURI + +Sets the base URI prefix for token-specific URIs. The base URI is concatenated with token-specific URIs set via setTokenURI. Does not affect the default URI used when no token-specific URI is set. + + +{`function setBaseURI(string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### setTokenURI + +Sets the token-specific URI for a given token ID. Sets tokenURIs[_tokenId] to the provided string and emits a URI event with the full computed URI. The emitted URI is the concatenation of baseURI and the token-specific URI. + + +{`function setTokenURI(uint256 _tokenId, string memory _tokenURI) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when multiple token types are transferred. +
+ +
+ Signature: + +{`event TransferBatch( +address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a single token type is transferred. +
+ +
+ Signature: + +{`event TransferSingle( +address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value +);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when the URI for token type `_id` changes to `_value`. +
+ +
+ Signature: + +{`event URI(string _value, uint256 indexed _id);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ **Title:** LibERC1155 — ERC-1155 Library Provides internal functions and storage layout for ERC-1155 multi-token logic. Thrown when insufficient balance for a transfer or burn operation. Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. This library is intended to be used by custom facets to integrate with ERC-1155 functionality. +
+ +
+ Signature: + +error ERC1155InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _tokenId); + +
+
+ +
+ Thrown when array lengths don't match in batch operations. +
+ +
+ Signature: + +error ERC1155InvalidArrayLength(uint256 _idsLength, uint256 _valuesLength); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC1155InvalidSender(address _sender); + +
+
+ +
+ Thrown when missing approval for an operator. +
+ +
+ Signature: + +error ERC1155MissingApprovalForAll(address _operator, address _owner); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC1155Mod } from "@compose-protocol/diamond/facets/ERC1155/ERC1155Mod.sol"; + +contract MyFacet { + address immutable diamondProxy; + + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; + } + + function mintTokens(address _to, uint256 _id, uint256 _amount) external { + IERC1155Mod(diamondProxy).mint(_to, _id, _amount); + } + + function transferTokens(address _from, address _to, uint256 _id, uint256 _amount) external { + IERC1155Mod(diamondProxy).safeTransferFrom(_from, _to, _id, _amount, ""); + } +}`} + + +## Best Practices + + +- Implement robust access control for minting and burning functions if required by your diamond's architecture. +- Ensure proper validation of receiver addresses, especially when interacting with other contracts, by implementing ERC1155Receiver logic. +- Always check balances before attempting to transfer or burn tokens to prevent `ERC1155InsufficientBalance` errors. + + +## Integration Notes + + +The ERC1155Mod utilizes a predefined storage slot within the diamond's storage layout to manage ERC-1155 token balances, approvals, and URI information. Facets interacting with this module should call its functions through the diamond proxy. The `getStorage` function can be used to access the underlying storage struct for read-only operations or for advanced integration, provided the caller understands the storage layout and potential for concurrent modification. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC1155/_category_.json b/website/docs/library/token/ERC1155/_category_.json new file mode 100644 index 00000000..cdb57d9a --- /dev/null +++ b/website/docs/library/token/ERC1155/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-1155", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC1155/index" + } +} diff --git a/website/docs/library/token/ERC1155/index.mdx b/website/docs/library/token/ERC1155/index.mdx new file mode 100644 index 00000000..24f5b890 --- /dev/null +++ b/website/docs/library/token/ERC1155/index.mdx @@ -0,0 +1,30 @@ +--- +title: "ERC-1155" +description: "ERC-1155 multi-token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-1155 multi-token implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx new file mode 100644 index 00000000..385a6ffd --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20BurnFacet.mdx @@ -0,0 +1,232 @@ +--- +sidebar_position: 99 +title: "ERC20BurnFacet" +description: "Burn ERC-20 tokens within a Compose diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC-20 tokens within a Compose diamond. + + + +- Allows burning of ERC-20 tokens directly from the diamond. +- Supports burning from the caller's balance (`burn`). +- Supports burning from another account's balance with prior allowance (`burnFrom`). +- Emits standard `Transfer` events to the zero address upon successful burns. + + +## Overview + +The ERC20BurnFacet enables the burning of ERC-20 tokens directly within a Compose diamond. It provides functions to burn tokens from the caller's balance or from another account using allowances, ensuring compliance with the ERC-20 standard by emitting Transfer events to the zero address. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific amount of tokens from the caller's balance. Emits a Transfer event to the zero address. + + +{`function burn(uint256 _value) external;`} + + +**Parameters:** + + + +--- +### burnFrom + +Burns tokens from another account, deducting from the caller's allowance. Emits a Transfer event to the zero address. + + +{`function burnFrom(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BurnFacet} from "@compose/contracts/facets/ERC20/IERC20BurnFacet.sol"; + +contract ERC20BurnConsumer { + address internal diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function consumeBurn(address _tokenAddress, uint256 _amount) external { + // Assuming the ERC20BurnFacet is registered for the token address + // Function selector for burn is 0x17b03b88 + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(bytes4(keccak256("burn(address,uint256)")), _tokenAddress, _amount)); + require(success, "Burn failed"); + } + + function consumeBurnFrom(address _tokenAddress, address _from, uint256 _amount) external { + // Function selector for burnFrom is 0x51789f0c + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(bytes4(keccak256("burnFrom(address,address,uint256)")), _tokenAddress, _from, _amount)); + require(success, "BurnFrom failed"); + } +}`} + + +## Best Practices + + +- Ensure the `ERC20BurnFacet` is properly registered in the diamond's facet registry for the relevant ERC-20 token addresses. +- Use `burnFrom` only after an allowance has been set using `ERC20ApproveFacet`. +- Handle potential `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` errors appropriately in consumer contracts. + + +## Security Considerations + + +This facet relies on the underlying ERC-20 token contract's balance and allowance checks. Ensure the `_amount` to be burned does not exceed the caller's balance or allowance, as enforced by the facet's error conditions. No reentrancy concerns are present as the functions do not make external calls after state changes. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx new file mode 100644 index 00000000..799b587b --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Facet.mdx @@ -0,0 +1,545 @@ +--- +sidebar_position: 99 +title: "ERC20Facet" +description: "Implements the ERC-20 token standard." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements the ERC-20 token standard. + + + +- Implements the core ERC-20 standard functions. +- Supports token transfers and `transferFrom` with allowance checks. +- Emits standard `Transfer` and `Approval` events. +- Allows querying token metadata and balances. + + +## Overview + +This facet provides a standard ERC-20 token interface for Compose diamonds. It handles token metadata, supply, balances, allowances, and transfers, enabling fungible token functionality within the diamond. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### name + +Returns the name of the token. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### decimals + +Returns the number of decimals used for token precision. + + +{`function decimals() external view returns (uint8);`} + + +**Returns:** + + + +--- +### totalSupply + +Returns the total supply of tokens. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the balance of a specific account. + + +{`function balanceOf(address _account) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Returns the remaining number of tokens that a spender is allowed to spend on behalf of an owner. + + +{`function allowance(address _owner, address _spender) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves a spender to transfer up to a certain amount of tokens on behalf of the caller. Emits an Approval event. + + +{`function approve(address _spender, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers tokens to another address. Emits a Transfer event. + + +{`function transfer(address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers tokens on behalf of another account, provided sufficient allowance exists. Emits a Transfer event and decreases the spender's allowance. + + +{`function transferFrom(address _from, address _to, uint256 _value) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when an account has insufficient balance for a transfer or burn. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when a spender tries to use more than the approved allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Facet} from "@compose/contracts/src/facets/ERC20/IERC20Facet.sol"; + +contract ERC20Consumer { + IERC20Facet public erc20Facet; + + constructor(address _erc20FacetAddress) { + erc20Facet = IERC20Facet(_erc20FacetAddress); + } + + function getTokenName() public view returns (string memory) { + return erc20Facet.name(); + } + + function checkBalance(address _account) public view returns (uint256) { + return erc20Facet.balanceOf(_account); + } + + function approveSpending(address _spender, uint256 _amount) public { + erc20Facet.approve(_spender, _amount); + } +}`} + + +## Best Practices + + +- Ensure the `ERC20Facet` is initialized with correct token metadata (name, symbol, decimals) during diamond deployment. +- Manage allowances carefully, especially when approving large amounts or indefinite spending. +- Implement access control on functions that modify token supply or ownership if required by your tokenomics. + + +## Security Considerations + + +Standard ERC-20 vulnerabilities apply. Ensure proper input validation for addresses and amounts. Be cautious with `approve` calls to prevent unintended allowance grants. Reentrancy is mitigated by the diamond proxy pattern and the facet's internal logic. Function calls like `transfer` and `transferFrom` should be guarded against the sender having insufficient balance or allowance respectively using the provided custom errors. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx new file mode 100644 index 00000000..7e73a4fe --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/ERC20Mod.mdx @@ -0,0 +1,430 @@ +--- +sidebar_position: 99 +title: "ERC20Mod" +description: "Standard ERC-20 token logic for Compose diamonds." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20/ERC20Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Standard ERC-20 token logic for Compose diamonds. + + + +- Implements standard ERC-20 `transfer`, `approve`, `transferFrom`, `mint`, and `burn` functions. +- Manages ERC-20 token balances and allowances through dedicated storage. +- Provides internal helper functions for ERC-20 operations, promoting reusability. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Mod provides the core functions and storage layout for implementing the ERC-20 token standard within a Compose diamond. It ensures composability by adhering to standard patterns for token transfers, approvals, minting, and burning, allowing facets to integrate and extend ERC-20 functionality safely. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; + string symbol; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves a spender to transfer tokens on behalf of the caller. Sets the allowance for the spender. + + +{`function approve(address _spender, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns tokens from a specified address. Decreases both total supply and the sender's balance. + + +{`function burn(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-20 storage struct. Uses inline assembly to bind the storage struct to the fixed storage position. + + +{`function getStorage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints new tokens to a specified address. Increases both total supply and the recipient's balance. + + +{`function mint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers tokens from the caller to another address. Updates balances directly without allowance mechanism. + + +{`function transfer(address _to, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers tokens from one address to another using an allowance. Deducts the spender's allowance and updates balances. + + +{`function transferFrom(address _from, address _to, uint256 _value) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a spender tries to spend more than their allowance. +
+ +
+ Signature: + +error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + +
+
+ +
+ Thrown when a sender attempts to transfer or burn more tokens than their balance. +
+ +
+ Signature: + +error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Mod } from "@compose/modules/erc20/ERC20Mod.sol"; + +contract MyERC20Facet { + struct Storage { + ERC20Mod.ERC20Storage erc20; + } + + Storage instance; + + function transferTokens(address to, uint256 amount) external { + instance.erc20.transfer(msg.sender, to, amount); + } + + function approveTokens(address spender, uint256 amount) external { + instance.erc20.approve(msg.sender, spender, amount); + } + + function mintTokens(address recipient, uint256 amount) external { + instance.erc20.mint(recipient, amount); + } + + function burnTokens(address from, uint256 amount) external { + instance.erc20.burn(from, amount); + } + + function getAllowance(address owner, address spender) external view returns (uint256) { + return instance.erc20.allowance(owner, spender); + } +}`} + + +## Best Practices + + +- Ensure the `ERC20Storage` struct is correctly initialized in the diamond's storage layout. +- Always use the provided `transferFrom` function for token movements involving allowances to maintain state integrity. +- Handle custom errors like `ERC20InsufficientBalance` and `ERC20InsufficientAllowance` in your facet logic. + + +## Integration Notes + + +The ERC20Mod uses a fixed storage slot for its `ERC20Storage` struct, accessible via the `getStorage` function. Facets integrating this module must include this struct in their own storage layout and ensure it's properly bound to the correct slot. All ERC-20 state changes (balances, allowances, total supply) are managed internally by the module and are immediately visible to other facets interacting with the diamond. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20/_category_.json b/website/docs/library/token/ERC20/ERC20/_category_.json new file mode 100644 index 00000000..bd8d3da5 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/ERC20/index" + } +} diff --git a/website/docs/library/token/ERC20/ERC20/index.mdx b/website/docs/library/token/ERC20/ERC20/index.mdx new file mode 100644 index 00000000..d3993e36 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20/index.mdx @@ -0,0 +1,37 @@ +--- +title: "ERC-20" +description: "ERC-20 fungible token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 fungible token implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx new file mode 100644 index 00000000..ba968049 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.mdx @@ -0,0 +1,390 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableFacet" +description: "Manages cross-chain ERC20 token transfers and minting/burning." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages cross-chain ERC20 token transfers and minting/burning. + + + +- Enables cross-chain minting and burning of ERC20 tokens. +- Restricts `crosschainMint` and `crosschainBurn` functions to addresses with the `trusted-bridge` role. +- Utilizes inline assembly for efficient storage access via the diamond storage pattern. + + +## Overview + +The ERC20BridgeableFacet enables secure cross-chain operations for ERC20 tokens. It allows trusted bridges to mint tokens on one chain and burn them on another. This facet leverages the diamond's storage pattern for efficient access to ERC20 and access control configurations. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +--- +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +### State Variables + + + +## Functions + +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) external;`} + + +**Parameters:** + + + +--- +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) external view;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "./interfaces/IERC20BridgeableFacet.sol"; + +contract Deployer { + // Assume diamond is deployed and selectors are registered + address internal immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function mintCrosschain(address _token, address _to, uint256 _amount) external { + bytes4 selector = IERC20BridgeableFacet.crosschainMint.selector; + // Call through the diamond proxy + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _token, _to, _amount)); + require(success, "Crosschain mint failed"); + } + + function burnCrosschain(address _token, address _from, uint256 _amount) external { + bytes4 selector = IERC20BridgeableFacet.crosschainBurn.selector; + // Call through the diamond proxy + (bool success, ) = diamondAddress.call(abi.encodeWithSelector(selector, _token, _from, _amount)); + require(success, "Crosschain burn failed"); + } +}`} + + +## Best Practices + + +- Initialize the `trusted-bridge` role in AccessControl for addresses authorized to call `crosschainMint` and `crosschainBurn`. +- Ensure that the ERC20 token contract is correctly deployed and accessible to the diamond. +- Use `getERC20Storage` and `getAccessControlStorage` to retrieve necessary configuration data. + + +## Security Considerations + + +The `crosschainMint` and `crosschainBurn` functions are protected by the `trusted-bridge` role, preventing unauthorized cross-chain operations. Input validation is performed by internal checks, including verifying the caller's role and ensuring valid recipient/sender addresses. Reentrancy is not a direct concern as these functions do not make external calls to untrusted contracts. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx new file mode 100644 index 00000000..57d9f71d --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.mdx @@ -0,0 +1,421 @@ +--- +sidebar_position: 99 +title: "ERC20BridgeableMod" +description: "Enables cross-chain token transfers and management." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Bridgeable/ERC20BridgeableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enables cross-chain token transfers and management. + + + +- Cross-chain token minting and burning capabilities. +- Access control for trusted bridge operators. +- Explicit error handling for invalid operations. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC20Bridgeable module facilitates secure cross-chain token operations. It manages trusted bridge addresses and handles the logic for burning and minting tokens across different chains, ensuring controlled and auditable inter-chain asset movements. This module is crucial for applications requiring decentralized cross-chain functionality. + +--- + +## Storage + +### AccessControlStorage + + +{`struct AccessControlStorage { + mapping(address account => mapping(bytes32 role => bool hasRole)) hasRole; +}`} + + +--- +### ERC20Storage + +ERC-8042 compliant storage struct for ERC20 token data. storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; +}`} + + +### State Variables + + + +## Functions + +### checkTokenBridge + +Internal check to check if the bridge (caller) is trusted. Reverts if caller is zero or not in the AccessControl `trusted-bridge` role. + + +{`function checkTokenBridge(address _caller) view;`} + + +**Parameters:** + + + +--- +### crosschainBurn + +Cross-chain burn — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainBurn(address _from, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### crosschainMint + +Cross-chain mint — callable only by an address having the `trusted-bridge` role. + + +{`function crosschainMint(address _account, uint256 _value) ;`} + + +**Parameters:** + + + +--- +### getAccessControlStorage + +helper to return AccessControlStorage at its diamond slot + + +{`function getAccessControlStorage() pure returns (AccessControlStorage storage s);`} + + +--- +### getERC20Storage + +Returns the ERC20 storage struct from the predefined diamond storage slot. Uses inline assembly to set the storage slot reference. + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +**Returns:** + + + +## Events + + + +
+ Emitted when a crosschain transfer burns tokens. +
+ +
+ Signature: + +{`event CrosschainBurn(address indexed _from, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are minted via a cross-chain bridge. +
+ +
+ Signature: + +{`event CrosschainMint(address indexed _to, uint256 _amount, address indexed _sender);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when tokens are transferred between two addresses. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the account does not have a specific role. +
+ +
+ Signature: + +error AccessControlUnauthorizedAccount(address _account, bytes32 _role); + +
+
+ + +
+ Signature: + +error ERC20InsufficientBalance(address _from, uint256 _accountBalance, uint256 _value); + +
+
+ +
+ Revert when caller is not a trusted bridge. +
+ +
+ Signature: + +error ERC20InvalidBridgeAccount(address _caller); + +
+
+ +
+ Revert when caller address is invalid. +
+ +
+ Signature: + +error ERC20InvalidCallerAddress(address _caller); + +
+
+ +
+ /// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions Revert when a provided receiver is invalid(e.g,zero address) . +
+ +
+ Signature: + +error ERC20InvalidReciever(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSender(address _sender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20BridgeableFacet} from "../interfaces/IERC20BridgeableFacet.sol"; +import {IDiamondStorage} from "../interfaces/IDiamondStorage.sol"; + +contract ERC20BridgeableConsumerFacet { + address immutable DIAMOND_ADDRESS; + + constructor(address diamondAddress) { + DIAMOND_ADDRESS = diamondAddress; + } + + function consumeCrosschainMint(address _to, uint256 _amount) external { + IERC20BridgeableFacet(DIAMOND_ADDRESS).crosschainMint(_to, _amount); + } + + function consumeCrosschainBurn(address _from, uint256 _amount) external { + IERC20BridgeableFacet(DIAMOND_ADDRESS).crosschainBurn(_from, _amount); + } +}`} + + +## Best Practices + + +- Ensure only addresses with the `trusted-bridge` role can call `crosschainBurn` and `crosschainMint`. +- Validate `_to` and `_from` addresses to prevent sending tokens to zero or invalid addresses. +- Handle `ERC20InsufficientBalance` and `ERC20InvalidReciever` errors gracefully. + + +## Integration Notes + + +This module interacts with the diamond's storage through predefined slots for AccessControl and ERC20 state. The `getAccessControlStorage` and `getERC20Storage` functions provide direct access to these structs. The `checkTokenBridge` internal function enforces access control by verifying the caller's role in the AccessControl storage. No specific storage slot ordering is mandated for this module itself, but it relies on the underlying diamond storage structure. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json new file mode 100644 index 00000000..03768f44 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Bridgeable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/ERC20Bridgeable/index" + } +} diff --git a/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx new file mode 100644 index 00000000..a85206ad --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Bridgeable/index.mdx @@ -0,0 +1,30 @@ +--- +title: "ERC-20 Bridgeable" +description: "ERC-20 Bridgeable extension for ERC-20 tokens." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 Bridgeable extension for ERC-20 tokens. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx new file mode 100644 index 00000000..f6137591 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitFacet.mdx @@ -0,0 +1,339 @@ +--- +sidebar_position: 99 +title: "ERC20PermitFacet" +description: "EIP-2612 compliant ERC-20 permit functionality." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +EIP-2612 compliant ERC-20 permit functionality. + + + +- Implements EIP-2612 permit functionality for ERC-20 tokens. +- Enables gasless approvals by allowing users to sign allowance requests off-chain. +- Utilizes nonces and domain separators to prevent replay attacks and ensure signature validity. + + +## Overview + +The ERC20PermitFacet enables gasless approvals for ERC-20 tokens by implementing EIP-2612's permit functionality. Users can grant allowances to spenders via signed messages, which can then be submitted by any party to the diamond, bypassing the need for the user to pay gas for the approval transaction. + +--- + +## Storage + +### ERC20Storage + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +--- +### ERC20PermitStorage + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +### State Variables + + + +## Functions + +### nonces + +Returns the current nonce for an owner. This value changes each time a permit is used. + + +{`function nonces(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for permit. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() external view returns (bytes32);`} + + +**Returns:** + + + +--- +### permit + +Sets the allowance for a spender via a signature. This function implements EIP-2612 permit functionality. + + +{`function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s +) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( + address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+ +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import { DiamondLoupeFacet } from "@openzeppelin/contracts/facets/DiamondLoupeFacet.sol"; +import { FacetNames } from "@openzeppelin/contracts/facets/FacetNames.sol"; + +// Assume Diamond interface and DiamondProxy are deployed elsewhere +interface IDiamond { + function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external; + function facetAddress(bytes4 _functionSelector) external view returns (address _facetAddress); + function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory _selectors); + function facets() external view returns (Facet[] memory _facets); +} + +interface IERC20PermitFacet { + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; + function nonces(address owner) external view returns (uint256); + function DOMAIN_SEPARATOR() external view returns (bytes32); +} + +contract ERC20PermitDeployer { + // ... deployment logic ... + + function grantPermit(address _diamondProxyAddress, address _tokenAddress, address _spender, uint256 _amount, uint256 _deadline) public { + // Fetch nonce and domain separator from the diamond + IERC20PermitFacet permitFacet = IERC20PermitFacet(_diamondProxyAddress); + bytes32 domainSeparator = permitFacet.DOMAIN_SEPARATOR(); + uint256 nonce = permitFacet.nonces(msg.sender); + + // Construct the permit message hash + bytes32 digest = keccak256( + abi.encode( IERC20Permit.permitHash(), msg.sender, _spender, _amount, nonce, _deadline) + ); + + // Sign the digest (this would typically be done off-chain) + // For demonstration, assume \`v\`, \`r\`, \`s\` are obtained from an external signature + uint8 v; + bytes32 r; + bytes32 s; + + // Submit the permit to the diamond + // Note: This assumes the ERC20 token contract is accessible and has an \`approve\` function + // and that the diamond proxy is configured to route permit calls to the ERC20PermitFacet. + permitFacet.permit(_tokenAddress, _spender, _amount, _deadline, v, r, s); + } +}`} + + +## Best Practices + + +- Integrate the `ERC20PermitFacet` into your diamond, ensuring its function selectors are correctly routed. +- Store the `DOMAIN_SEPARATOR` and `nonces` mapping within the diamond's storage or a dedicated facet for consistent access. +- Off-chain signing of permit messages is crucial for enabling gasless approvals. The signed data is then submitted on-chain by any party. + + +## Security Considerations + + +Ensure the `DOMAIN_SEPARATOR` is correctly computed and unique per chain ID and contract instance. The `nonces` mapping must be managed carefully to prevent permit reuse. Validate the signature parameters (`v`, `r`, `s`) and the `owner` address before setting allowances. Access to the `permit` function should be controlled if necessary, although typically it's intended to be permissionless once the signature is valid. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx new file mode 100644 index 00000000..68d39d9e --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/ERC20PermitMod.mdx @@ -0,0 +1,281 @@ +--- +sidebar_position: 99 +title: "ERC20PermitMod" +description: "ERC-2612 Permit and domain separator logic" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC20/ERC20Permit/ERC20PermitMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +ERC-2612 Permit and domain separator logic + + + +- Implements ERC-2612 Permit functionality for gasless token approvals. +- Manages and provides the domain separator for signature validation. +- Includes explicit error handling for invalid signatures and disallowed spenders. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic for ERC-2612 permit functionality, enabling gasless approvals via signed messages. It manages the domain separator and permit validation, ensuring secure and efficient token approvals within a diamond. + +--- + +## Storage + +### ERC20PermitStorage + +storage-location: erc8042:compose.erc20.permit + + +{`struct ERC20PermitStorage { + mapping(address owner => uint256) nonces; +}`} + + +--- +### ERC20Storage + +storage-location: erc8042:compose.erc20 + + +{`struct ERC20Storage { + mapping(address owner => uint256 balance) balanceOf; + uint256 totalSupply; + mapping(address owner => mapping(address spender => uint256 allowance)) allowance; + uint8 decimals; + string name; +}`} + + +### State Variables + + + +## Functions + +### DOMAIN_SEPARATOR + +Returns the domain separator used in the encoding of the signature for {permit}. This value is unique to a contract and chain ID combination to prevent replay attacks. + + +{`function DOMAIN_SEPARATOR() view returns (bytes32);`} + + +**Returns:** + + + +--- +### getERC20Storage + + +{`function getERC20Storage() pure returns (ERC20Storage storage s);`} + + +--- +### getPermitStorage + + +{`function getPermitStorage() pure returns (ERC20PermitStorage storage s);`} + + +--- +### permit + +Validates a permit signature and sets allowance. Emits Approval event; must be emitted by the calling facet/contract. + + +{`function permit(address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval is made for a spender by an owner. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 _value);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC20InvalidSpender(address _spender); + +
+
+ +
+ Thrown when a permit signature is invalid or expired. +
+ +
+ Signature: + +error ERC2612InvalidSignature( +address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s +); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {ERC20PermitMod} from "@compose-protocol/diamond-contracts/modules/erc20/ERC20PermitMod.sol"; + +contract MyTokenFacet { + using ERC20PermitMod for ERC20PermitMod.PermitStorage; + + ERC20PermitMod.PermitStorage public permitStorage; + + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external + returns (bool) + { + // Ensure the permit storage is initialized or managed appropriately + // For example, if it's part of a larger diamond storage struct: + // ERC20PermitMod.PermitStorage storage ps = ERC20PermitMod.getPermitStorage(diamondStorage); + // permitStorage.permit(owner, spender, value, deadline, v, r, s); + + // For a standalone facet, you'd manage permitStorage directly: + return permitStorage.permit(owner, spender, value, deadline, v, r, s); + } + + // Other ERC20 functions and facet logic... +}`} + + +## Best Practices + + +- Ensure the `ERC20PermitMod.PermitStorage` is correctly initialized and accessible within your facet or diamond storage. +- Implement access control for the `permit` function if necessary, though ERC-2612 is designed to be owner-driven. +- Verify the `deadline` parameter to prevent stale permit approvals. + + +## Integration Notes + + +This module requires access to its `PermitStorage` struct, which should be managed either within the diamond's main storage or a dedicated slot. The `permit` function within this module validates the signature and updates the allowance; the calling facet is responsible for emitting the `Approval` event if required by the ERC-20 implementation standard. The domain separator is crucial for preventing cross-chain or cross-contract replay attacks. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC20/ERC20Permit/_category_.json b/website/docs/library/token/ERC20/ERC20Permit/_category_.json new file mode 100644 index 00000000..7932c4df --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20 Permit", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/ERC20Permit/index" + } +} diff --git a/website/docs/library/token/ERC20/ERC20Permit/index.mdx b/website/docs/library/token/ERC20/ERC20Permit/index.mdx new file mode 100644 index 00000000..1ee93f31 --- /dev/null +++ b/website/docs/library/token/ERC20/ERC20Permit/index.mdx @@ -0,0 +1,30 @@ +--- +title: "ERC-20 Permit" +description: "ERC-20 Permit extension for ERC-20 tokens." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 Permit extension for ERC-20 tokens. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC20/_category_.json b/website/docs/library/token/ERC20/_category_.json new file mode 100644 index 00000000..0e078cb1 --- /dev/null +++ b/website/docs/library/token/ERC20/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-20", + "position": 1, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC20/index" + } +} diff --git a/website/docs/library/token/ERC20/index.mdx b/website/docs/library/token/ERC20/index.mdx new file mode 100644 index 00000000..0bb39d2d --- /dev/null +++ b/website/docs/library/token/ERC20/index.mdx @@ -0,0 +1,37 @@ +--- +title: "ERC-20" +description: "ERC-20 fungible token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-20 fungible token implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx new file mode 100644 index 00000000..36e2b49f --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Facet.mdx @@ -0,0 +1,513 @@ +--- +sidebar_position: 99 +title: "ERC6909Facet" +description: "Manage ERC-6909 compliant token balances and operator roles." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-6909 compliant token balances and operator roles. + + + +- Implements core ERC-6909 transfer and allowance logic. +- Supports operator roles for delegated spending. +- Provides `getStorage` for direct state inspection (use with caution). +- Emits standard `Transfer` and `Approval` events. + + +## Overview + +This facet implements the ERC-6909 standard, providing functionality to manage token balances, allowances, and operator relationships within a Compose diamond. It enables standard token transfers and operator approvals, enhancing composability for tokenized assets. + +--- + +## Storage + +### ERC6909Storage + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### balanceOf + +Owner balance of an id. + + +{`function balanceOf(address _owner, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### allowance + +Spender allowance of an id. + + +{`function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isOperator + +Checks if a spender is approved by an owner as an operator. + + +{`function isOperator(address _owner, address _spender) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transfer + +Transfers an amount of an id from the caller to a receiver. + + +{`function transfer(address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### transferFrom + +Transfers an amount of an id from a sender to a receiver. + + +{`function transferFrom(address _sender, address _receiver, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _spender, uint256 _id, uint256 _amount) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _spender, bool _approved) external returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer( + address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ + +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ + +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Facet} from "@compose/facets/erc6909/IERC6909Facet.sol"; +import {IERC165} from "@compose/core/IERC165.sol"; + +contract MyDiamond is IERC165 { + // ... other facet interfaces and implementations ... + + function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { + // ... other interface checks ... + if (interfaceId == type(IERC6909Facet).interfaceId) { + return true; + } + return false; + } + + // Example of calling transfer from another facet or contract + function performTransfer(address _to, uint256 _amount, uint256 _id) external { + // Assuming IERC6909Facet is registered and callable + (bool success, ) = address(this).call(abi.encodeWithSelector(IERC6909Facet.transfer.selector, + _to, _amount, _id)); + require(success, "Transfer failed"); + } + + // Example of approving an operator + function grantOperatorRole(address _operator, uint256 _id) external { + // Assuming IERC6909Facet is registered and callable + (bool success, ) = address(this).call(abi.encodeWithSelector(IERC6909Facet.setOperator.selector, + _operator, _id, true)); + require(success, "Set operator failed"); + } +}`} + + +## Best Practices + + +- Initialize the facet with appropriate access controls during diamond deployment. +- Ensure token IDs and amounts are validated before calling transfer or approve functions. +- Store the facet's address securely and manage upgrades carefully to maintain state integrity. + + +## Security Considerations + + +Input validation is crucial; ensure `_to`, `_id`, and `_amount` parameters are valid to prevent unexpected behavior. The `transferFrom` function requires careful management of allowances to prevent unauthorized spending. Access to `setOperator` should be restricted to prevent malicious operators from being set. Direct access to storage via `getStorage` bypasses function logic and should only be used in controlled environments. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx new file mode 100644 index 00000000..f0640918 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/ERC6909Mod.mdx @@ -0,0 +1,525 @@ +--- +sidebar_position: 99 +title: "ERC6909Mod" +description: "Implements ERC-6909 minimal multi-token logic." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC6909/ERC6909/ERC6909Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Implements ERC-6909 minimal multi-token logic. + + + +- Supports multiple token IDs within a single contract context. +- Implements standard ERC-6909 functions for token management. +- Allows for flexible operator approvals to facilitate trading and management. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic and storage for implementing the ERC-6909 standard. It enables the management of multiple token types within a single contract, supporting minting, burning, transfers, and operator approvals. By adhering to the ERC-6909 standard, diamonds can offer flexible and interoperable multi-token functionality. + +--- + +## Storage + +### ERC6909Storage + +storage-location: erc8042:compose.erc6909 + + +{`struct ERC6909Storage { + mapping(address owner => mapping(uint256 id => uint256 amount)) balanceOf; + mapping(address owner => mapping(address spender => mapping(uint256 id => uint256 amount))) allowance; + mapping(address owner => mapping(address spender => bool)) isOperator; +}`} + + +### State Variables + + + +## Functions + +### approve + +Approves an amount of an id to a spender. + + +{`function approve(address _owner, address _spender, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### burn + +Burns `_amount` of token id `_id` from `_from`. + + +{`function burn(address _from, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns a pointer to the ERC-6909 storage struct. Uses inline assembly to access the storage slot defined by STORAGE_POSITION. + + +{`function getStorage() pure returns (ERC6909Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints `_amount` of token id `_id` to `_to`. + + +{`function mint(address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +--- +### setOperator + +Sets or removes a spender as an operator for the caller. + + +{`function setOperator(address _owner, address _spender, bool _approved) ;`} + + +**Parameters:** + + + +--- +### transfer + +Transfers `_amount` of token id `_id` from `_from` to `_to`. Allowance is not deducted if it is `type(uint256).max` Allowance is not deducted if `_by` is an operator for `_from`. + + +{`function transfer(address _by, address _from, address _to, uint256 _id, uint256 _amount) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when an approval occurs. +
+ +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _amount);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when an operator is set. +
+ +
+ Signature: + +{`event OperatorSet(address indexed _owner, address indexed _spender, bool _approved);`} + +
+ +
+ Parameters: + +
+
+ +
+ Emitted when a transfer occurs. +
+ +
+ Signature: + +{`event Transfer( +address _caller, address indexed _sender, address indexed _receiver, uint256 indexed _id, uint256 _amount +);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the spender has insufficient allowance. +
+ +
+ Signature: + +error ERC6909InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the sender has insufficient balance. +
+ +
+ Signature: + +error ERC6909InsufficientBalance(address _sender, uint256 _balance, uint256 _needed, uint256 _id); + +
+
+ +
+ Thrown when the approver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidApprover(address _approver); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSender(address _sender); + +
+
+ +
+ Thrown when the spender address is invalid. +
+ +
+ Signature: + +error ERC6909InvalidSpender(address _spender); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC6909Mod} from "@compose/contracts/modules/erc6909/IERC6909Mod.sol"; +import {ERC6909ModStorage} from "@compose/contracts/modules/erc6909/ERC6909ModStorage.sol"; + +contract MyERC6909Facet { + + function approve(address _spender, uint256 _amount, uint256 _id) external { + IERC6909Mod(msg.sender).approve(_spender, _amount, _id); + } + + function transfer(address _from, address _to, uint256 _amount, uint256 _id) external { + IERC6909Mod(msg.sender).transfer(_from, _to, _amount, _id); + } + + function mint(address _to, uint256 _amount, uint256 _id) external { + IERC6909Mod(msg.sender).mint(_to, _amount, _id); + } + + function burn(address _from, uint256 _amount, uint256 _id) external { + IERC6909Mod(msg.sender).burn(_from, _amount, _id); + } + + function setOperator(address _operator, bool _approved) external { + IERC6909Mod(msg.sender).setOperator(_operator, _approved); + } +}`} + + +## Best Practices + + +- Ensure appropriate access control is implemented in facets calling `mint` and `burn` functions. +- Handle custom errors like `ERC6909InsufficientBalance` and `ERC6909InsufficientAllowance` gracefully in calling facets. +- Be mindful of operator approvals; they grant significant spending power for specific token IDs. + + +## Integration Notes + + +The ERC6909Mod uses a dedicated storage slot defined by `STORAGE_POSITION`. Facets interacting with this module should be aware of the `ERC6909ModStorage` struct layout and ensure no storage collisions occur. The `getStorage` function provides a direct pointer to this storage for internal use by facets. Changes to allowances or balances are managed within this module's storage and are visible to all facets interacting with the diamond. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC6909/ERC6909/_category_.json b/website/docs/library/token/ERC6909/ERC6909/_category_.json new file mode 100644 index 00000000..d4d084dc --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC6909/ERC6909/index" + } +} diff --git a/website/docs/library/token/ERC6909/ERC6909/index.mdx b/website/docs/library/token/ERC6909/ERC6909/index.mdx new file mode 100644 index 00000000..c902a388 --- /dev/null +++ b/website/docs/library/token/ERC6909/ERC6909/index.mdx @@ -0,0 +1,30 @@ +--- +title: "ERC-6909" +description: "ERC-6909 minimal multi-token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-6909 minimal multi-token implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC6909/_category_.json b/website/docs/library/token/ERC6909/_category_.json new file mode 100644 index 00000000..42f1101f --- /dev/null +++ b/website/docs/library/token/ERC6909/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-6909", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC6909/index" + } +} diff --git a/website/docs/library/token/ERC6909/index.mdx b/website/docs/library/token/ERC6909/index.mdx new file mode 100644 index 00000000..dab5e87f --- /dev/null +++ b/website/docs/library/token/ERC6909/index.mdx @@ -0,0 +1,23 @@ +--- +title: "ERC-6909" +description: "ERC-6909 minimal multi-token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-6909 minimal multi-token implementations. + + + + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx new file mode 100644 index 00000000..bb0491a5 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721BurnFacet.mdx @@ -0,0 +1,200 @@ +--- +sidebar_position: 99 +title: "ERC721BurnFacet" +description: "Burn ERC721 tokens within a Compose diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721BurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Burn ERC721 tokens within a Compose diamond. + + + +- Burns ERC721 tokens, effectively destroying them. +- Emits standard `Transfer` events for burned tokens (from owner to address(0)). +- Utilizes inline assembly to access the correct storage slot for ERC721 state. + + +## Overview + +The ERC721BurnFacet provides the functionality to destroy ERC721 tokens. It integrates with the diamond proxy pattern to offer a composable way to manage token lifecycle, specifically the burning of owned tokens. This facet ensures that burned tokens are correctly removed from tracking and associated events are emitted. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IDiamondCut} from "@compose/diamond/contracts/interfaces/IDiamondCut.sol"; +import {IERC721BurnFacet} from "./interfaces/IERC721BurnFacet.sol"; + +contract Deployer { + address immutable diamondAddress; + + constructor(address _diamondAddress) { + diamondAddress = _diamondAddress; + } + + function addBurnFacet(address _burnFacetImplementation) external { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = IERC721BurnFacet.getStorage.selector; + selectors[1] = IERC721BurnFacet.burn.selector; + + IDiamondCut(diamondAddress).diamondCut([ + IDiamondCut.FacetCut({ + facetAddress: _burnFacetImplementation, + action: IDiamondCut.FacetCutAction.ADD, + isUnion: false, + selectors: selectors + }) + ], address(0), ""); + } + + function burnToken(uint256 _tokenId) external { + IERC721BurnFacet(diamondAddress).burn(_tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `ERC721BurnFacet` is added to the diamond with the correct selectors. +- Call `burn` only for tokens owned by the caller or for which the caller has sufficient approval. +- Understand the storage layout by calling `getStorage` if direct interaction with underlying ERC721 state is required. + + +## Security Considerations + + +The `burn` function requires the caller to be the owner of the token or have explicit approval. Ensure that the diamond's access control mechanisms correctly enforce these ownership and approval checks before allowing the `burn` function to be executed. Reentrancy is not a concern as `burn` does not make external calls before state changes. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx new file mode 100644 index 00000000..0f5bb7c3 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Facet.mdx @@ -0,0 +1,615 @@ +--- +sidebar_position: 99 +title: "ERC721Facet" +description: "Manage ERC-721 tokens and metadata within a diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Facet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-721 tokens and metadata within a diamond. + + + +- Implements the ERC-721 standard for non-fungible tokens. +- Supports token transfers, ownership tracking, and approvals. +- Provides `tokenURI` for metadata retrieval. +- Includes internal transfer logic for robust state management. + + +## Overview + +The ERC721Facet provides a standard implementation for ERC-721 token functionality. It enables core operations such as token transfers, ownership queries, approvals, and metadata retrieval. This facet can be integrated into a diamond to offer a composable and upgradeable NFT collection. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### name + +Returns the token collection name. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the token collection symbol. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by a given address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns true if an operator is approved to manage all of an owner's assets. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer the given token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes permission for an operator to manage all caller's assets. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking if the receiver can handle ERC-721 tokens. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Facet} from "@compose/contracts/src/facets/ERC721/IERC721Facet.sol"; + +contract MyDiamond is IERC721Facet { + // ... other facet interfaces and implementations + + address constant ERC721_FACET_ADDRESS = address(0xabc...); // Address where ERC721Facet is deployed + + function name() external view override returns (string memory) { + return IERC721Facet(ERC721_FACET_ADDRESS).name(); + } + + function symbol() external view override returns (string memory) { + return IERC721Facet(ERC721_FACET_ADDRESS).symbol(); + } + + function balanceOf(address _owner) external view override returns (uint256) { + return IERC721Facet(ERC721_FACET_ADDRESS).balanceOf(_owner); + } + + function ownerOf(uint256 _tokenId) external view override returns (address) { + return IERC721Facet(ERC721_FACET_ADDRESS).ownerOf(_tokenId); + } + + function transferFrom(address _from, address _to, uint256 _tokenId) external override { + IERC721Facet(ERC721_FACET_ADDRESS).transferFrom(_from, _to, _tokenId); + } + + // ... other functions +}`} + + +## Best Practices + + +- Initialize the ERC721Facet with a unique storage slot using `STORAGE_POSITION`. +- Grant necessary permissions for `approve` and `setApprovalForAll` operations. +- Ensure the receiver contract implements `onERC721Received` for `safeTransferFrom` if applicable. + + +## Security Considerations + + +The `internalTransferFrom` function includes checks for ownership and approvals. `safeTransferFrom` adds a layer of security by verifying receiver contract compatibility. Ensure that access control for `approve` and `setApprovalForAll` functions is correctly managed by the diamond's access control mechanism. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx new file mode 100644 index 00000000..a4241422 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/ERC721Mod.mdx @@ -0,0 +1,362 @@ +--- +sidebar_position: 99 +title: "ERC721Mod" +description: "Manage ERC-721 tokens within a Compose diamond." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721/ERC721Mod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC-721 tokens within a Compose diamond. + + + +- Supports core ERC-721 operations: mint, burn, and transfer. +- Utilizes diamond storage for persistent and shared state management. +- Includes specific error types for common ERC-721 failures. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The ERC721Mod provides essential internal logic for minting, burning, and transferring ERC-721 compliant tokens directly within a Compose diamond. It leverages the diamond storage pattern to ensure state is managed consistently and accessibly by any compliant facet, promoting composability and reducing boilerplate code for ERC-721 functionality. + +--- + +## Storage + +### ERC721Storage + + +{`struct ERC721Storage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256 balance) balanceOf; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a specific ERC-721 token. Reverts if the token does not exist. Clears ownership and approval. + + +{`function burn(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (ERC721Storage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### setMetadata + + +{`function setMetadata(string memory _name, string memory _symbol, string memory _baseURI) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers ownership of a token ID from one address to another. Validates ownership, approval, and receiver address before updating state. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks sufficient approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid (e.g., zero address). +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Mod } from "@compose/modules/ERC721Mod.sol"; +import {ERC721Storage } from "@compose/modules/ERC721Mod.sol"; + +contract MyERC721Facet { + IERC721Mod public immutable erc721Mod; + + constructor(address _erc721ModAddress) { + erc721Mod = IERC721Mod(_erc721ModAddress); + } + + function mintNewToken(address _to, uint256 _tokenId) external { + // Assume _isApprovedOrOwner check is handled externally or by the module + erc721Mod.mint(_to, _tokenId); + } + + function transferMyToken(address _from, address _to, uint256 _tokenId) external { + // Assume _isApprovedOrOwner check is handled externally or by the module + erc721Mod.transferFrom(_from, _to, _tokenId); + } + + function burnMyToken(uint256 _tokenId) external { + // Assume _isApprovedOrOwner check is handled externally or by the module + erc721Mod.burn(_tokenId); + } +}`} + + +## Best Practices + + +- Implement robust access control within facets calling this module to ensure only authorized users can perform token operations. +- Always validate receiver addresses to prevent accidental token loss. +- Be aware that `setMetadata` is present but undescribed; consult the implementation if metadata management is critical. + + +## Integration Notes + + +The ERC721Mod interacts with a predefined storage slot for its `ERC721Storage` struct. Facets integrating this module can access the current state of ERC-721 tokens using the `getStorage` function. Any operations performed by this module directly modify the diamond's storage, making state changes visible to all other facets. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721/_category_.json b/website/docs/library/token/ERC721/ERC721/_category_.json new file mode 100644 index 00000000..219beb4e --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC721/ERC721/index" + } +} diff --git a/website/docs/library/token/ERC721/ERC721/index.mdx b/website/docs/library/token/ERC721/ERC721/index.mdx new file mode 100644 index 00000000..83f6f725 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721/index.mdx @@ -0,0 +1,37 @@ +--- +title: "ERC-721" +description: "ERC-721 non-fungible token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-721 non-fungible token implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx new file mode 100644 index 00000000..9ca60927 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.mdx @@ -0,0 +1,202 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableBurnFacet" +description: "Manage ERC721 token burning and enumeration" +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manage ERC721 token burning and enumeration + + + +- Enables burning of ERC721 tokens. +- Maintains enumeration integrity by removing burned tokens from tracking. +- Provides explicit error handling for non-existent tokens and insufficient approvals. + + +## Overview + +This facet provides functionality to burn ERC721 tokens. It integrates with the ERC721 enumerable standard, ensuring that burned tokens are correctly removed from tracking and ownership records. This facet is essential for managing the lifecycle of tokens within a Compose diamond. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) a token, removing it from enumeration tracking. + + +{`function burn(uint256 _tokenId) external;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ +
+ Thrown when the caller lacks approval to operate on the token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableBurnFacet} from "@compose-protocol/diamond-contracts/contracts/facets/ERC721/IERC721EnumerableBurnFacet.sol"; + +contract Usage { + IERC721EnumerableBurnFacet public immutable erc721EnumerableBurnFacet; + + constructor(address diamondAddress) { + // Assume diamondAddress is the address of the deployed Compose diamond + erc721EnumerableBurnFacet = IERC721EnumerableBurnFacet(diamondAddress); + } + + function burnToken(uint256 tokenId) public { + // Ensure the caller has the necessary approvals or ownership + // For simplicity, this example assumes the caller is authorized + erc721EnumerableBurnFacet.burn(tokenId); + } +}`} + + +## Best Practices + + +- Ensure the `burn` function is called with appropriate access control (e.g., token owner or approved address). +- Integrate this facet into a diamond that already implements the core ERC721 and ERC721Enumerable interfaces. +- Understand that burning a token is an irreversible action. + + +## Security Considerations + + +The `burn` function requires careful access control to prevent unauthorized token destruction. Ensure that only the token owner or an address with explicit approval can call this function. The `ERC721NonexistentToken` and `ERC721InsufficientApproval` errors provide clear feedback on invalid burn attempts. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx new file mode 100644 index 00000000..0a00b735 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.mdx @@ -0,0 +1,686 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableFacet" +description: "Enumerable ERC-721 implementation for tracking token ownership and metadata." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enumerable ERC-721 implementation for tracking token ownership and metadata. + + + +- Full ERC-721 compliance with enumerable extensions. +- Efficient querying of token supply, balances, and owner information. +- Supports metadata retrieval via `tokenURI`. +- Includes internal transfer logic for composability within the diamond. + + +## Overview + +This facet provides a complete ERC-721 implementation with enumerable extensions, allowing efficient querying of token supply, balances, ownership, and approvals. It surfaces standard ERC-721 functions alongside methods for retrieving token IDs by owner index. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### name + +Returns the name of the token collection. + + +{`function name() external view returns (string memory);`} + + +**Returns:** + + + +--- +### symbol + +Returns the symbol of the token collection. + + +{`function symbol() external view returns (string memory);`} + + +**Returns:** + + + +--- +### tokenURI + +Provide the metadata URI for a given token ID. + + +{`function tokenURI(uint256 _tokenId) external view returns (string memory);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### totalSupply + +Returns the total number of tokens in existence. + + +{`function totalSupply() external view returns (uint256);`} + + +**Returns:** + + + +--- +### balanceOf + +Returns the number of tokens owned by an address. + + +{`function balanceOf(address _owner) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### ownerOf + +Returns the owner of a given token ID. + + +{`function ownerOf(uint256 _tokenId) public view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### tokenOfOwnerByIndex + +Returns a token ID owned by a given address at a specific index. + + +{`function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### getApproved + +Returns the approved address for a given token ID. + + +{`function getApproved(uint256 _tokenId) external view returns (address);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### isApprovedForAll + +Returns whether an operator is approved for all tokens of an owner. + + +{`function isApprovedForAll(address _owner, address _operator) external view returns (bool);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### approve + +Approves another address to transfer a specific token ID. + + +{`function approve(address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### setApprovalForAll + +Approves or revokes an operator to manage all tokens of the caller. + + +{`function setApprovalForAll(address _operator, bool _approved) external;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token from one address to another. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token, checking for receiver contract compatibility. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;`} + + +**Parameters:** + + + +--- +### safeTransferFrom + +Safely transfers a token with additional data. + + +{`function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external;`} + + +**Parameters:** + + + +## Events + + + + +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event Approval(address indexed _owner, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ + +
+ Signature: + +{`event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);`} + +
+ +
+
+ +## Errors + + + + +
+ Signature: + +error ERC721InvalidOwner(address _owner); + +
+
+ + +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ + +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ + +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ + +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ + +
+ Signature: + +error ERC721InvalidApprover(address _approver); + +
+
+ + +
+ Signature: + +error ERC721InvalidOperator(address _operator); + +
+
+ + +
+ Signature: + +error ERC721OutOfBoundsIndex(address _owner, uint256 _index); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721Enumerable } from "@compose-protocol/core/src/interfaces/tokens/IERC721Enumerable.sol"; +import { DiamondUtils } from "@compose-protocol/core/src/utils/DiamondUtils.sol"; + +contract ERC721EnumerableConsumer { + IERC721Enumerable public erc721Facet; + + constructor(address diamondAddress) { + erc721Facet = IERC721Enumerable(diamondAddress); + } + + function getTokenName() external view returns (string memory) { + return erc721Facet.name(); + } + + function getTotalSupply() external view returns (uint256) { + return erc721Facet.totalSupply(); + } + + function getOwnerOfToken(uint256 tokenId) external view returns (address) { + return erc721Facet.ownerOf(tokenId); + } + + function getTokenByIndex(address owner, uint256 index) external view returns (uint256) { + return erc721Facet.tokenOfOwnerByIndex(owner, index); + } +}`} + + +## Best Practices + + +- Initialize the facet with explicit ownership or access control mechanisms if required by your application's security model. +- Leverage `tokenOfOwnerByIndex` for iterating through a specific owner's tokens, ensuring indices are within bounds to prevent errors. +- When performing transfers, prefer `safeTransferFrom` to ensure receiver contracts are compatible with ERC-721 tokens. + + +## Security Considerations + + +Ensure that access control for functions like `approve` and `setApprovalForAll` is correctly implemented at the diamond level. The `internalTransferFrom` function is intended for internal use and should not be exposed directly. Be mindful of reentrancy risks if custom logic interacts with token transfers. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx new file mode 100644 index 00000000..694dc202 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.mdx @@ -0,0 +1,338 @@ +--- +sidebar_position: 99 +title: "ERC721EnumerableMod" +description: "Manages enumerable ERC-721 token state and operations." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/ERC721/ERC721Enumerable/ERC721EnumerableMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages enumerable ERC-721 token state and operations. + + + +- Manages token ownership and enumeration state for ERC-721 tokens. +- Supports minting new tokens and burning existing ones, updating enumeration lists accordingly. +- Handles token transfers by updating sender and receiver enumeration data. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides core logic for enumerable ERC-721 functionality within a Compose diamond. It enables facets to mint, burn, and transfer tokens while maintaining accurate enumeration lists. By centralizing this logic, it ensures consistent state management and simplifies facet development. + +--- + +## Storage + +### ERC721EnumerableStorage + + +{`struct ERC721EnumerableStorage { + mapping(uint256 tokenId => address owner) ownerOf; + mapping(address owner => uint256[] ownerTokens) ownerTokens; + mapping(uint256 tokenId => uint256 ownerTokensIndex) ownerTokensIndex; + uint256[] allTokens; + mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndex; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(uint256 tokenId => address approved) approved; + string name; + string symbol; + string baseURI; +}`} + + +### State Variables + + + +## Functions + +### burn + +Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. Reverts if the token does not exist or if the sender is not authorized. + + +{`function burn(uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +--- +### getStorage + +Returns the ERC-721 enumerable storage struct from its predefined slot. Uses inline assembly to point to the correct diamond storage position. + + +{`function getStorage() pure returns (ERC721EnumerableStorage storage s);`} + + +**Returns:** + + + +--- +### mint + +Mints a new ERC-721 token to the specified address, adding it to enumeration lists. Reverts if the receiver address is zero or if the token already exists. + + +{`function mint(address _to, uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### transferFrom + +Transfers a token ID from one address to another, updating enumeration data. Validates ownership, approval, and receiver address before state updates. + + +{`function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) ;`} + + +**Parameters:** + + + +## Events + + + +
+ Emitted when ownership of a token changes, including minting and burning. +
+ +
+ Signature: + +{`event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);`} + +
+ +
+ Parameters: + +
+
+
+ +## Errors + + + +
+ Thrown when the sender is not the owner of the token. +
+ +
+ Signature: + +error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + +
+
+ +
+ Thrown when an operator lacks approval to manage a token. +
+ +
+ Signature: + +error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + +
+
+ +
+ Thrown when the receiver address is invalid. +
+ +
+ Signature: + +error ERC721InvalidReceiver(address _receiver); + +
+
+ +
+ Thrown when the sender address is invalid. +
+ +
+ Signature: + +error ERC721InvalidSender(address _sender); + +
+
+ +
+ Thrown when attempting to interact with a non-existent token. +
+ +
+ Signature: + +error ERC721NonexistentToken(uint256 _tokenId); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IERC721EnumerableMod} from "./interfaces/IERC721EnumerableMod.sol"; +import {ERC721EnumerableMod} from "./ERC721EnumerableMod.sol"; + +contract MyERC721Facet { + IERC721EnumerableMod private constant _ERC721_ENUMERABLE_MOD = IERC721EnumerableMod(address(this)); + + function mintToken(address _to, uint256 _tokenId) external { + _ERC721_ENUMERABLE_MOD.mint(_to, _tokenId); + } + + function burnToken(uint256 _tokenId) external { + _ERC721_ENUMERABLE_MOD.burn(_tokenId); + } + + function transferToken(address _from, address _to, uint256 _tokenId) external { + _ERC721_ENUMERABLE_MOD.transferFrom(_from, _to, _tokenId); + } +}`} + + +## Best Practices + + +- Ensure proper access control within your facet before calling module functions like `mint` and `burn`. +- Validate all input parameters (e.g., `_to` address for `mint`) to prevent unexpected reverts from the module. +- Be aware that state changes made by this module are persistent and affect all facets interacting with ERC-721 enumerable data. + + +## Integration Notes + + +The ERC721EnumerableMod interacts with a predefined storage slot within the diamond to manage its state. Facets using this module should import the relevant interface and cast the diamond's address to it. The `getStorage` function can be used by facets to access the raw storage struct directly if needed for complex operations or auditing, though direct manipulation is discouraged. State changes made by this module (e.g., token minting, burning, transfers) are visible to all facets that access the ERC-721 enumerable storage. + + +
+ +
+ + diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json new file mode 100644 index 00000000..fdc633f9 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721 Enumerable", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC721/ERC721Enumerable/index" + } +} diff --git a/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx new file mode 100644 index 00000000..6c35acf4 --- /dev/null +++ b/website/docs/library/token/ERC721/ERC721Enumerable/index.mdx @@ -0,0 +1,37 @@ +--- +title: "ERC-721 Enumerable" +description: "ERC-721 Enumerable extension for ERC-721 tokens." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-721 Enumerable extension for ERC-721 tokens. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/ERC721/_category_.json b/website/docs/library/token/ERC721/_category_.json new file mode 100644 index 00000000..8ee4f288 --- /dev/null +++ b/website/docs/library/token/ERC721/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "ERC-721", + "position": 2, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/ERC721/index" + } +} diff --git a/website/docs/library/token/ERC721/index.mdx b/website/docs/library/token/ERC721/index.mdx new file mode 100644 index 00000000..24a9e4be --- /dev/null +++ b/website/docs/library/token/ERC721/index.mdx @@ -0,0 +1,30 @@ +--- +title: "ERC-721" +description: "ERC-721 non-fungible token implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-721 non-fungible token implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/Royalty/RoyaltyFacet.mdx b/website/docs/library/token/Royalty/RoyaltyFacet.mdx new file mode 100644 index 00000000..f3ab86f1 --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyFacet.mdx @@ -0,0 +1,171 @@ +--- +sidebar_position: 99 +title: "RoyaltyFacet" +description: "Manages token royalties according to ERC-2981." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyFacet.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages token royalties according to ERC-2981. + + + +- Implements ERC-2981 `royaltyInfo` function. +- Supports token-specific royalty configurations. +- Falls back to a default royalty setting when token-specific royalties are not defined. +- Royalty calculation based on sale price in basis points. + + +## Overview + +The RoyaltyFacet implements the ERC-2981 standard, allowing tokens to specify royalty payments on secondary sales. It provides functions to retrieve royalty information for a given token ID and sale price, supporting both token-specific and default royalty configurations. + +--- + +## Storage + +### RoyaltyInfo + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### royaltyInfo + +Returns royalty information for a given token and sale price. Returns token-specific royalty if set, otherwise falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) + external + view + returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyFacet} from "@compose-protocol/diamond-contracts/contracts/facets/RoyaltyFacet.sol"; +import {IDiamondProxy} from "@compose-protocol/diamond-contracts/contracts/interfaces/IDiamondProxy.sol"; + +contract RoyaltyConsumer { + address immutable diamondProxy; + bytes4 private constant ROYALTY_INFO_SELECTOR = IRoyaltyFacet.royaltyInfo.selector; + + constructor(address _diamondProxy) { + diamondProxy = _diamondProxy; + } + + function getTokenRoyalty(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount) { + (bool success, bytes memory data) = diamondProxy.call(abi.encodeWithSelector(ROYALTY_INFO_SELECTOR, _tokenId, _salePrice)); + require(success, "RoyaltyFacet: royaltyInfo call failed"); + (receiver, royaltyAmount) = abi.decode(data, (address, uint256)); + return (receiver, royaltyAmount); + } +}`} + + +## Best Practices + + +- Initialize the RoyaltyFacet with default royalty settings during diamond deployment. +- Ensure appropriate access control is configured for setting default royalties if applicable. +- When upgrading, ensure the storage layout of the RoyaltyFacet remains compatible. + + +## Security Considerations + + +The `royaltyInfo` function is read-only and does not pose reentrancy risks. Access control for setting default royalties should be strictly managed to prevent unauthorized modifications. Ensure the `STORAGE_POSITION` for royalty storage is unique and not conflicting with other facets. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/RoyaltyMod.mdx b/website/docs/library/token/Royalty/RoyaltyMod.mdx new file mode 100644 index 00000000..38f66d01 --- /dev/null +++ b/website/docs/library/token/Royalty/RoyaltyMod.mdx @@ -0,0 +1,340 @@ +--- +sidebar_position: 99 +title: "RoyaltyMod" +description: "Manages ERC-2981 royalties for tokens and defaults." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/token/Royalty/RoyaltyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Manages ERC-2981 royalties for tokens and defaults. + + + +- Implements ERC-2981 `royaltyInfo` logic, supporting token-specific and default royalties. +- Provides functions to set, update, and delete royalty configurations. +- Includes error handling for invalid royalty parameters and receivers. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +This module provides the core logic for implementing the ERC-2981 royalty standard within a Compose diamond. It handles setting and querying both token-specific and default royalty information, ensuring compliant royalty distributions. + +--- + +## Storage + +### RoyaltyInfo + +Structure containing royalty information. **Properties** + + +{`struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; +}`} + + +--- +### RoyaltyStorage + +storage-location: erc8042:compose.erc2981 + + +{`struct RoyaltyStorage { + RoyaltyInfo defaultRoyaltyInfo; + mapping(uint256 tokenId => RoyaltyInfo) tokenRoyaltyInfo; +}`} + + +### State Variables + + + +## Functions + +### deleteDefaultRoyalty + +Removes default royalty information. After calling this function, royaltyInfo will return (address(0), 0) for tokens without specific royalty. + + +{`function deleteDefaultRoyalty() ;`} + + +--- +### getStorage + +Returns the royalty storage struct from its predefined slot. Uses inline assembly to access diamond storage location. + + +{`function getStorage() pure returns (RoyaltyStorage storage s);`} + + +**Returns:** + + + +--- +### resetTokenRoyalty + +Resets royalty information for a specific token to use the default setting. Clears token-specific royalty storage, causing fallback to default royalty. + + +{`function resetTokenRoyalty(uint256 _tokenId) ;`} + + +**Parameters:** + + + +--- +### royaltyInfo + +Queries royalty information for a given token and sale price. Returns token-specific royalty or falls back to default royalty. Royalty amount is calculated as a percentage of the sale price using basis points. Implements the ERC-2981 royaltyInfo function logic. + + +{`function royaltyInfo(uint256 _tokenId, uint256 _salePrice) view returns (address receiver, uint256 royaltyAmount);`} + + +**Parameters:** + + + +**Returns:** + + + +--- +### setDefaultRoyalty + +Sets the default royalty information that applies to all tokens. Validates receiver and fee, then updates default royalty storage. + + +{`function setDefaultRoyalty(address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +--- +### setTokenRoyalty + +Sets royalty information for a specific token, overriding the default. Validates receiver and fee, then updates token-specific royalty storage. + + +{`function setTokenRoyalty(uint256 _tokenId, address _receiver, uint96 _feeNumerator) ;`} + + +**Parameters:** + + + +## Errors + + + +
+ Thrown when default royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyalty(uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when default royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidDefaultRoyaltyReceiver(address _receiver); + +
+
+ +
+ Thrown when token-specific royalty fee exceeds 100% (10000 basis points). +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyalty(uint256 _tokenId, uint256 _numerator, uint256 _denominator); + +
+
+ +
+ Thrown when token-specific royalty receiver is the zero address. +
+ +
+ Signature: + +error ERC2981InvalidTokenRoyaltyReceiver(uint256 _tokenId, address _receiver); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {IRoyaltyMod} from "@compose/modules/RoyaltyMod.sol"; +import {IDiamondProxy} from "@compose/diamond/IDiamondProxy.sol"; + +contract RoyaltyFacet { + address immutable DIAMOND_PROXY; + + constructor(address _diamondProxy) { + DIAMOND_PROXY = _diamondProxy; + } + + function setRoyalty(uint256 _tokenId, address _receiver, uint16 _fee) external { + IRoyaltyMod(DIAMOND_PROXY).setTokenRoyalty(_tokenId, _receiver, _fee); + } + + function getRoyaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address, uint256) { + return IRoyaltyMod(DIAMOND_PROXY).royaltyInfo(_tokenId, _salePrice); + } +}`} + + +## Best Practices + + +- Use `setDefaultRoyalty` sparingly, as it impacts all tokens without specific configurations. +- Validate `_receiver` and `_fee` for both `setTokenRoyalty` and `setDefaultRoyalty` to prevent invalid royalty setups. +- Be aware that calling `resetTokenRoyalty` will revert the token to using the default royalty settings. + + +## Integration Notes + + +The RoyaltyMod stores its state in a dedicated slot within the diamond's storage. Facets can access this storage via the `getStorage` function. `royaltyInfo` queries token-specific royalties first, falling back to default royalties if no token-specific configuration is found. Deleting the default royalty means `royaltyInfo` will return `(address(0), 0)` for tokens without specific royalty settings. + + +
+ +
+ + diff --git a/website/docs/library/token/Royalty/_category_.json b/website/docs/library/token/Royalty/_category_.json new file mode 100644 index 00000000..cb6b460f --- /dev/null +++ b/website/docs/library/token/Royalty/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Royalty", + "position": 5, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/Royalty/index" + } +} diff --git a/website/docs/library/token/Royalty/index.mdx b/website/docs/library/token/Royalty/index.mdx new file mode 100644 index 00000000..d570d73b --- /dev/null +++ b/website/docs/library/token/Royalty/index.mdx @@ -0,0 +1,30 @@ +--- +title: "Royalty" +description: "ERC-2981 royalty standard implementations." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + ERC-2981 royalty standard implementations. + + + + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/token/_category_.json b/website/docs/library/token/_category_.json new file mode 100644 index 00000000..3f26c2ce --- /dev/null +++ b/website/docs/library/token/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Token Standards", + "position": 3, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/token/index" + } +} diff --git a/website/docs/library/token/index.mdx b/website/docs/library/token/index.mdx new file mode 100644 index 00000000..17b1ae16 --- /dev/null +++ b/website/docs/library/token/index.mdx @@ -0,0 +1,51 @@ +--- +title: "Token Standards" +description: "Token standard implementations for Compose diamonds." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Token standard implementations for Compose diamonds. + + + + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + } + size="medium" + /> + diff --git a/website/docs/library/utils/NonReentrancyMod.mdx b/website/docs/library/utils/NonReentrancyMod.mdx new file mode 100644 index 00000000..721197d9 --- /dev/null +++ b/website/docs/library/utils/NonReentrancyMod.mdx @@ -0,0 +1,135 @@ +--- +sidebar_position: 99 +title: "NonReentrancyMod" +description: "Enforces non-reentrant execution within diamond functions." +gitSource: "https://github.com/Perfect-Abstractions/Compose/tree/main/src/libraries/NonReentrancyMod.sol" +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Badge from '@site/src/components/ui/Badge'; +import Callout from '@site/src/components/ui/Callout'; +import CalloutBox from '@site/src/components/ui/CalloutBox'; +import Accordion, { AccordionGroup } from '@site/src/components/ui/Accordion'; +import PropertyTable from '@site/src/components/api/PropertyTable'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; +import CodeShowcase from '@site/src/components/code/CodeShowcase'; +import RelatedDocs from '@site/src/components/docs/RelatedDocs'; +import WasThisHelpful from '@site/src/components/docs/WasThisHelpful'; +import LastUpdated from '@site/src/components/docs/LastUpdated'; +import ReadingTime from '@site/src/components/docs/ReadingTime'; +import GradientText from '@site/src/components/ui/GradientText'; +import GradientButton from '@site/src/components/ui/GradientButton'; + + +Enforces non-reentrant execution within diamond functions. + + + +- Prevents reentrant function calls to protect state integrity. +- Uses a simple uint256 storage slot for the reentrancy lock. +- Composable with any facet that manages its own storage. + + + +This module provides internal functions for use in your custom facets. Import it to access shared logic and storage. + + +## Overview + +The NonReentrancyMod provides a robust mechanism to prevent reentrant calls within your diamond's facets. By integrating this module, you ensure that a function, once entered, cannot be re-entered before it has completed its execution, safeguarding against unexpected state changes and security vulnerabilities. + +--- + +## Storage + +### State Variables + + + +## Functions + +### enter + +How to use as a library in user facets How to use as a modifier in user facets This unlocks the entry into a function + + +{`function enter() ;`} + + +--- +### exit + +This locks the entry into a function + + +{`function exit() ;`} + + +## Errors + + + +
+ Function selector - 0x43a0d067 +
+ +
+ Signature: + +error Reentrancy(); + +
+
+
+ +## Usage Example + + +{`pragma solidity ^0.8.30; + +import {LibNonReentrancy} from "@compose/contracts/src/modules/non-reentrancy/LibNonReentrancy.sol"; + +contract MyFacet { + using LibNonReentrancy for uint256; + + uint256 internal _lock; + + /** + * @notice Performs an action that must not be reentrant. + */ + function sensitiveAction() external { + _lock.enter(); // Lock the function + // ... perform sensitive operations ... + _lock.exit(); // Unlock the function + } +}`} + + +## Best Practices + + +- Always call `_lock.enter()` at the beginning of a function and `_lock.exit()` at the end. +- Ensure `_lock.exit()` is called even in cases of early returns or reverts to prevent permanent locking. +- Use the `Reentrancy` custom error for explicit error handling. + + +## Integration Notes + + +The NonReentrancyMod is designed to be integrated as a library. It relies on a single `uint256` variable within the facet's storage to act as the reentrancy lock. This variable must be initialized (though not necessarily explicitly) and managed by the facet using the `enter` and `exit` functions. The state of this lock is local to the facet and does not directly interact with or modify diamond-level storage beyond what the facet itself controls. + + +
+ +
+ + diff --git a/website/docs/library/utils/_category_.json b/website/docs/library/utils/_category_.json new file mode 100644 index 00000000..d9c087be --- /dev/null +++ b/website/docs/library/utils/_category_.json @@ -0,0 +1,10 @@ +{ + "label": "Utilities", + "position": 4, + "collapsible": true, + "collapsed": true, + "link": { + "type": "doc", + "id": "library/utils/index" + } +} diff --git a/website/docs/library/utils/index.mdx b/website/docs/library/utils/index.mdx new file mode 100644 index 00000000..2345aaad --- /dev/null +++ b/website/docs/library/utils/index.mdx @@ -0,0 +1,23 @@ +--- +title: "Utilities" +description: "Utility libraries and helpers for diamond development." +sidebar_class_name: hidden +--- + +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import Icon from '@site/src/components/ui/Icon'; + + + Utility libraries and helpers for diamond development. + + + + } + size="medium" + /> +