{"id":26477470,"url":"https://github.com/heyjonbray/pindao","last_synced_at":"2026-06-17T16:38:55.868Z","repository":{"id":283216877,"uuid":"951053382","full_name":"heyJonBray/PinDAO","owner":"heyJonBray","description":"A Community-Driven IPFS Pinning Protocol","archived":false,"fork":false,"pushed_at":"2025-03-19T05:26:24.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-19T06:23:11.772Z","etag":null,"topics":["dao","evm","ipfs"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heyJonBray.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-19T05:07:29.000Z","updated_at":"2025-03-19T05:26:27.000Z","dependencies_parsed_at":"2025-03-19T12:31:43.835Z","dependency_job_id":null,"html_url":"https://github.com/heyJonBray/PinDAO","commit_stats":null,"previous_names":["heyjonbray/pindao"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/heyJonBray/PinDAO","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyJonBray%2FPinDAO","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyJonBray%2FPinDAO/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyJonBray%2FPinDAO/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyJonBray%2FPinDAO/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heyJonBray","download_url":"https://codeload.github.com/heyJonBray/PinDAO/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyJonBray%2FPinDAO/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260482191,"owners_count":23015871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dao","evm","ipfs"],"created_at":"2025-03-20T00:47:04.569Z","updated_at":"2025-10-26T01:42:03.837Z","avatar_url":"https://github.com/heyJonBray.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pin.sol: A Decentralized ETH-Based IPFS Pinning Protocol 📌🌐\n\n## Abstract\n\nPin.sol is a decentralized protocol designed to incentivize persistent file storage on the InterPlanetary File System (IPFS) through direct ETH payments on Ethereum and compatible blockchains. By leveraging a decentralized network of storage providers, a fair node rotation queue system, and a reputation-based reward mechanism, Pin.sol ensures reliable file availability and contract-level access to IPFS nodes for increased composability.\n\n## Introduction\n\nIPFS has emerged as a powerful distributed file system, but it lacks native economic incentives to ensure long-term file persistence. When a file is uploaded to IPFS, there's no guarantee it will remain accessible unless someone actively \"pins\" it to their node.\n\nCurrent solutions fall into three categories:\n\n1. Centralized pinning services (creating single points of failure)\n2. Custom blockchain networks like Filecoin and Arweave (requiring migration to separate ecosystems)\n3. Self-hosted infrastructure (resource-intensive and technically complex)\n\nPin.sol introduces a simple, Ethereum-native solution where IPFS node operators are directly rewarded in ETH for providing reliable pinning services, while users can prioritize their content through market-based mechanisms, all without requiring a separate token economy.\n\n## Core Mechanisms 🛠️\n\n### Wallet-Linked IPFS Nodes\n\nEach participating IPFS node connects an Ethereum wallet and stake ETH as collateral to participate in the protocol. Nodes receive ETH payments for pinning files from users, with smart contracts tracking node participation, staking, and ensuring service delivery through community verification of pinned files.\n\n### Fair Queue System\n\nParticipating nodes are placed in a rotation queue to receive pinning assignments. After completing a job, the node moves to the back of the queue to ensure all participating nodes have fair access to earning opportunities. The base ETH cost per file is calculated based on file size and requested duration.\n\n### Reputation-Based Reward Distribution\n\nNodes build reputation based on reliability and service quality which determines the percentage of payment received from pin requests:\n\n- Highest reputation nodes receive 99% of payment\n- Lower reputation nodes receive proportionally less (down to 50%)\n\nRemaining percentage goes to the DAO treasury, with the DAO taking a base 1% of protocol revenue.\n\n### Dual Pricing Models\n\nPin.sol supports two complementary pricing models:\n\n#### 1. Standard Queue-Based System\n\nUsers submit pinning requests with required ETH payment based on file size and duration. The protocol automatically assigns files to the next node(s) in the queue based on the specified replication factor.\n\n#### 2. Direct Node Payments\n\nUsers can bypass the queue and pay nodes directly in ETH for long-term pinning, creating a decentralized marketplace where users and nodes negotiate terms. In direct agreements, node reputation does not affect payment percentage.\n\n### Escrow \u0026 Protection Mechanism\n\nFunds are held in escrow and are vested linearly for the duration that pinning service is provided for. If a node unpins content prematurely, the remaining escrowed ETH is returned to the payer with 5% of returned funds being awarded to users who report violations, creating community incentives to report bad actors.\n\n### Node Banning Mechanism\n\nNodes that consistently fail to meet protocol requirements face progressive penalties to reputation and earnings:\n\n🟡 **First offense:** Short-term ban. First offenses do not incur a reputation strike.\n\n🟠 **Repeated offenses:** Increasingly longer ban and reputation hit. The more a node misbehaves, the lower it's reputation gets (down to 50%).\n\n🔴 **Severe violations:** Perma-ban and slashing of staked ETH.\n\nBans are enforced transparently onchain, and appeals can be made through a decentralized governance process.\n\n✂️ Slashed ETH goes to the DAO treasury\n\n## Verifier Services 🕵️‍♀️\n\nThe Pin.sol protocol creates economic opportunities for **Verifiers** who maintain network integrity by checking IPFS for content availability promised by nodes and verifying that pinning commitments are being honored.\n\n**Verifiers:**\n🚨 Reporting nodes that fail to meet obligations\n🏆 Earning ETH rewards (5% of remaining escrow) for maintaining network health\n\n### How Verifier Services Work\n\n1. **Automated Monitoring**: Verifiers scan the network, checking actual availability of pinned content\n2. **Reputation Enforcement**: Trigger the protocol's reputation mechanisms for failing nodes\n3. **Economic Incentives**: Earn a percentage of returned funds when reporting violations\n\nThis creates a market for developers to build services around Pin.sol, such as IPFS availability monitors, node reputation tracking dashboards, and analytics tools,.\n\n## DAO Treasury and Fees\n\nPin.sol collects fees that go directly to the DAO treasury:\n\n1. **Queue-Based Assignments**: A percentage of each payment based on node reputation:\n\n   - 1% from highest reputation nodes (99% to node)\n   - Up to 50% from lowest reputation nodes (50% to node)\n\n2. **Direct Payments**: Flat 1% fee on all direct agreements between users and nodes\n\n## Comparison with Other Storage Solutions\n\n| Feature                        | Pin.sol                      | Filecoin                    | Arweave            | Centralized Pinning |\n| ------------------------------ | ---------------------------- | --------------------------- | ------------------ | ------------------- |\n| **Native Currency**            | ETH                          | FIL                         | AR                 | Various             |\n| **Smart Contract Integration** | Direct                       | Limited                     | Limited            | None                |\n| **Barrier to Entry**           | Low (standard IPFS node)     | High (specialized hardware) | Medium             | N/A                 |\n| **Payment Model**              | Queue-based + direct payment | Long-term contracts         | One-time perpetual | Subscription        |\n| **Node Selection**             | Fair queue rotation          | Auction/manual deals        | Miners compete     | Centralized         |\n| **Storage Verification**       | Community-driven             | Proof-of-Spacetime          | Proof-of-Access    | Centralized         |\n| **Content Addressing**         | IPFS CIDs                    | IPFS CIDs                   | Transaction-based  | Proprietary         |\n| **Node Economics**             | Reputation-based rewards     | Complex deal structure      | Endowment-based    | Fixed pricing       |\n| **Censorship Resistance**      | High                         | High                        | Very High          | Low                 |\n\n### Why Choose Pin.sol?\n\n1. **Ethereum Native**: Uses ETH directly, no need for token swaps or bridges\n2. **Fair Distribution**: Queue-based system ensures all nodes get opportunities\n3. **Incentive Alignment**: Reputation affects rewards, not job access\n4. **Low Barrier**: Anyone with an IPFS node can participate and earn\n5. **Transparent**: All commitments and payments are visible on-chain\n6. **Sustainable**: DAO treasury ensures long-term protocol development\n\n## Security \u0026 Sustainability\n\n- **Stake-Based Participation**: Required ETH staking ensures nodes have skin in the game\n- **Linear Vesting Model**: Payments release over time, aligning incentives for long-term storage\n- **Community Policing**: The 5% bounty for reporting violations creates distributed oversight\n- **Transparent Queue**: Fair job distribution prevents centralization\n- **Reputation Economics**: Better performance leads to higher earnings percentage\n- **Progressive Banning**: Repeat offenders face escalating penalties\n- **Sustainable Treasury**: Protocol fees fund ongoing development and maintenance\n\n---\n\n## Smart Contract Design\n\nBelow is an outline of proposed smart contract design for Pin.sol. This code is not tested and should not be used in a production environment.\n\n### Node Registration \u0026 Queue Management\n\nStructure of an onchain IPFS node\n\n```js\nstruct Node {\n    address payable wallet;   // Wallet address of node operator\n    string ipfsPeerId;        // Unique identifier for IPFS node\n    uint256 reputation;       // Score representing node reliability (0-100)\n    uint256 stakedAmount;     // Total ETH locked as collateral\n    uint256 lastActive;       // Timestamp of most recent activity\n    bool isActive;            // Whether node is currently eligible for assignments\n    uint256 bannedUntil;      // Timestamp when temporary ban expires (0 if not banned)\n    uint256 banCount;         // Number of times node has been banned\n    uint256 totalEarned;      // Total ETH earned through the protocol\n    mapping(bytes32 =\u003e bool) activePins;  // Currently pinned content\n}\n```\n\nImplementation of the node queue system.\n\n```js\naddress[] private nodeQueue;\nmapping(address =\u003e uint256) private nodeQueuePosition;\n```\n\nNew nodes register with their `peerId` by staking ETH and are placed at the end of the queue.\n\n```js\nfunction registerNode(string calldata ipfsPeerId) external payable {\n    require(msg.value \u003e= MINIMUM_STAKE, \"Insufficient stake\");\n    require(nodeQueuePosition[msg.sender] == 0, \"Already registered\");\n\n    nodes[msg.sender] = Node({\n        wallet: payable(msg.sender),\n        ipfsPeerId: ipfsPeerId,\n        reputation: INITIAL_REPUTATION,\n        stakedAmount: msg.value,\n        lastActive: block.timestamp,\n        isActive: true,\n        bannedUntil: 0,\n        banCount: 0,\n        totalEarned: 0\n    });\n\n    // Add node to the end of the queue\n    nodeQueue.push(msg.sender);\n    nodeQueuePosition[msg.sender] = nodeQueue.length;\n\n    emit NodeRegistered(msg.sender, ipfsPeerId, msg.value);\n}\n```\n\nNodes are rotated to the back of the queue after being assigned a pin request.\n\n```js\nfunction _rotateNodeInQueue(address node) internal {\n    uint256 pos = nodeQueuePosition[node];\n    require(pos \u003e 0, \"Node not in queue\");\n\n    // Remove node from current position\n    for (uint i = pos; i \u003c nodeQueue.length; i++) {\n        nodeQueue[i-1] = nodeQueue[i];\n        nodeQueuePosition[nodeQueue[i]] = i;\n    }\n\n    // Put node at end of queue\n    nodeQueue[nodeQueue.length - 1] = node;\n    nodeQueuePosition[node] = nodeQueue.length;\n}\n```\n\nContract gets the next available nodes from the queue\n\n```js\nfunction _getNextAvailableNodes(uint8 count) internal view returns (address[] memory) {\n    address[] memory availableNodes = new address[](count);\n    uint256 found = 0;\n\n    for (uint i = 0; i \u003c nodeQueue.length \u0026\u0026 found \u003c count; i++) {\n        address nodeAddr = nodeQueue[i];\n        if (nodes[nodeAddr].isActive \u0026\u0026 nodes[nodeAddr].bannedUntil \u003c block.timestamp) {\n            availableNodes[found] = nodeAddr;\n            found++;\n        }\n    }\n\n    require(found == count, \"Not enough available nodes\");\n    return availableNodes;\n}\n```\n\n### File Pinning Requests \u0026 Pricing\n\nStructure of an onchain pin request\n\n```js\nstruct PinRequest {\n    bytes32 cid;                 // Content Identifier (CID) of file\n    address payable user;        // Address of requestor\n    uint256 duration;            // Time period (in seconds) for pinning\n    uint256 fileSize;            // Size of file in bytes\n    uint256 payment;             // Total ETH payment\n    uint256 createdAt;           // Timestamp when request was submitted\n    bool fulfilled;              // Whether request has been assigned to nodes\n    uint8 replicationFactor;     // Number of nodes to pin the content\n}\n```\n\nThe price of a pin request is determined based on file size and duration the pin is requested for. DAO fees can be updated by governance proposals but is initialized at 1%.\n\n```js\nfunction calculateBasePrice(uint256 fileSize, uint256 duration) public pure returns (uint256) {\n    // Example pricing formula\n    // Base price = (fileSize in MB * 0.0001 ETH) * (duration in days / 30)\n    uint256 fileSizeInMB = fileSize / (1024 * 1024);\n    uint256 durationInDays = duration / 86400;\n\n    return (fileSizeInMB * 1e14 * durationInDays) / 30;\n}\n```\n\nPin requests are submitted with a `replicationFactor` that determines how many nodes should pin the file. Payment cost is `(fileSize * duration) * replicationFactor` and paid at the time of submission, after which the payment is split between the number of participating nodes and their respective fees (modified by reputation) are vested to them.\n\n```js\nfunction submitPinRequest(\n    bytes32 cid,\n    uint256 fileSize,\n    uint256 duration,\n    uint8 replicationFactor\n) external payable {\n    // Calculate minimum required payment\n    uint256 basePrice = calculateBasePrice(fileSize, duration);\n    uint256 totalPrice = basePrice * replicationFactor;\n\n    require(msg.value \u003e= totalPrice, \"Insufficient payment\");\n    require(duration \u003e= MIN_DURATION, \"Duration too short\");\n    require(replicationFactor \u003e 0, \"Must request at least one node\");\n\n    uint256 requestId = _nextRequestId++;\n\n    pinRequests[requestId] = PinRequest({\n        cid: cid,\n        user: payable(msg.sender),\n        duration: duration,\n        fileSize: fileSize,\n        payment: msg.value,\n        createdAt: block.timestamp,\n        fulfilled: false,\n        replicationFactor: replicationFactor\n    });\n\n    // Process the request immediately\n    _processRequest(requestId);\n\n    emit PinRequestSubmitted(requestId, cid, msg.sender, msg.value, duration);\n}\n```\n\n### Node Assignments \u0026 Reputation-Based Payments\n\nStructure of successful pin assignment to a node.\n\n```js\nstruct PinAssignment {\n    bytes32 cid;            // CID of pinned file\n    address node;           // Address of assigned node\n    address payable user;   // User who requested the pinning\n    uint256 startTime;      // When pinning began\n    uint256 endTime;        // When pinning should end\n    uint256 totalPayment;   // Total ETH allocated for this assignment\n    uint256 claimedAmount;  // ETH already claimed through vesting\n    bool active;            // Whether assignment is currently active\n}\n```\n\nPayment percentage is calculated based on node reputation. At max reputation (100) a node receives 99% of fees. At 0 reputation, a node gets 50% of the fees, with a linear scale between. Nodes with 0 reputation who receive another strike are banned from participation.\n\n```js\nfunction _calculatePaymentPercentage(uint256 reputation) internal pure returns (uint256) {\n    uint256 basePercentage = 50;\n    // Multiply reputation (0-100) by 49 (difference between min \u0026 max payment received) to get the variable portion\n    uint256 variablePercentage = (reputation * 49) / 100;\n\n    return basePercentage + variablePercentage;\n}\n```\n\nRequests are processed from the queue, selecting next available nodes and processing their reputation-based payment into a linear vesting schedule.\n\n```js\nfunction _processRequest(uint256 requestId) internal {\n    PinRequest storage request = pinRequests[requestId];\n    require(!request.fulfilled, \"Request already fulfilled\");\n\n    // Get the next available nodes\n    address[] memory selectedNodes = _getNextAvailableNodes(request.replicationFactor);\n\n    uint256 paymentPerNode = request.payment / request.replicationFactor;\n    uint256 daoFee = 0;\n\n    for (uint i = 0; i \u003c selectedNodes.length; i++) {\n        address node = selectedNodes[i];\n\n        // Calculate node's payment based on reputation\n        uint256 paymentPercentage = _calculatePaymentPercentage(nodes[node].reputation);\n        uint256 nodePayment = (paymentPerNode * paymentPercentage) / 100;\n\n        // Track DAO fee (remainder)\n        daoFee += paymentPerNode - nodePayment;\n\n        // Create assignment with vesting schedule\n        uint256 assignmentId = _nextAssignmentId++;\n\n        pinAssignments[assignmentId] = PinAssignment({\n            cid: request.cid,\n            node: node,\n            user: request.user,\n            startTime: block.timestamp,\n            endTime: block.timestamp + request.duration,\n            totalPayment: nodePayment,\n            claimedAmount: 0,\n            active: true\n        });\n\n        // Update node's active pins\n        nodes[node].activePins[request.cid] = true;\n\n        // Rotate node to back of queue\n        _rotateNodeInQueue(node);\n\n        emit PinAssigned(assignmentId, request.cid, node, nodePayment, request.duration);\n    }\n\n    // Send DAO fee to treasury\n    if (daoFee \u003e 0) {\n        (bool success, ) = daoTreasury.call{value: daoFee}(\"\");\n        require(success, \"DAO fee transfer failed\");\n        emit DAOFeeCollected(requestId, daoFee);\n    }\n\n    request.fulfilled = true;\n}\n```\n\n### Direct Payments\n\nIn addition to the queue system, users can select and pay a node directly.\n\n```js\nstruct DirectPayment {\n    bytes32 cid;                // Content Identifier\n    address payable node;       // Node providing service\n    address payable user;       // User paying for service\n    uint256 amount;             // Total ETH paid to node\n    // DAO fee initialized at 1% but can be modified by governance proposals\n    uint256 daoFee;             // Fee collected by DAO\n    uint256 startTime;          // When agreement began\n    uint256 endTime;            // When service should end\n    uint256 claimedAmount;      // ETH already claimed\n    bool active;                // Current status\n}\n```\n\nDirect payments are negotiated between both parties and bypasses reputation, meaning that nodes paid directly receive the full fee (minus DAO percentage).\n\n```js\nfunction payNodeDirectly(\n    address payable node,\n    bytes32 cid,\n    uint256 duration\n) external payable {\n    require(msg.value \u003e 0, \"Payment required\");\n    require(nodes[node].isActive, \"Node not active\");\n    require(nodes[node].bannedUntil \u003c block.timestamp, \"Node is banned\");\n\n    // Calculate basis points for DAO fee (100 = 1%)\n    uint256 daoFee = (msg.value * daoFeePercentage) / 10000;\n    uint256 nodePayment = msg.value - daoFee;\n\n    uint256 paymentId = _nextDirectPaymentId++;\n\n    directPayments[paymentId] = DirectPayment({\n        cid: cid,\n        node: node,\n        user: payable(msg.sender),\n        amount: nodePayment,\n        daoFee: daoFee,\n        startTime: block.timestamp,\n        endTime: block.timestamp + duration,\n        claimedAmount: 0,\n        active: true\n    });\n\n    // Update node's active pins\n    nodes[node].activePins[cid] = true;\n\n    // Send DAO fee\n    (bool success, ) = daoTreasury.call{value: daoFee}(\"\");\n    require(success, \"DAO fee transfer failed\");\n\n    emit DirectPaymentCreated(paymentId, cid, node, msg.sender, nodePayment, daoFee, duration);\n}\n```\n\n### Claiming Vested Payments\n\nAt any time during an active assignment, a node can claim the ETH currently being vested to them.\n\n```js\nfunction claimVestedPayment(uint256 assignmentId) external {\n    PinAssignment storage assignment = pinAssignments[assignmentId];\n\n    require(msg.sender == assignment.node, \"Not authorized\");\n    require(assignment.active, \"Assignment not active\");\n\n    // Calculate vested amount\n    uint256 totalDuration = assignment.endTime - assignment.startTime;\n    uint256 elapsed = block.timestamp - assignment.startTime;\n\n    uint256 vestedAmount;\n    if (block.timestamp \u003e= assignment.endTime) {\n        vestedAmount = assignment.totalPayment;\n    } else {\n        vestedAmount = (assignment.totalPayment * elapsed) / totalDuration;\n    }\n\n    uint256 claimableAmount = vestedAmount - assignment.claimedAmount;\n    require(claimableAmount \u003e 0, \"No funds to claim\");\n\n    // Update claimed amount\n    assignment.claimedAmount += claimableAmount;\n\n    // Transfer ETH to node\n    (bool success, ) = assignment.node.call{value: claimableAmount}(\"\");\n    require(success, \"Transfer failed\");\n\n    // Update node's total earned\n    nodes[assignment.node].totalEarned += claimableAmount;\n\n    emit PaymentClaimed(assignmentId, assignment.node, claimableAmount);\n}\n```\n\n### Verification \u0026 Slashing\n\nIf a node is found to have prematurely unpinned an item, or is offline long enough for IPFS garbage collection to remove the pin, **verifiers** can report them to the protocol.\n\n\u003e ⚠️ **NOTE:** The verification procedure needs a lot of work to ensure that it's reliable and doesn't maliciously target nodes who are behaving.\n\n```js\nfunction reportUnpinnedContent(bytes32 cid, address node, uint256 assignmentId) external {\n    PinAssignment storage assignment = pinAssignments[assignmentId];\n\n    require(assignment.cid == cid, \"CID mismatch\");\n    require(assignment.node == node, \"Node mismatch\");\n    require(assignment.active, \"Assignment not active\");\n\n    // Verification logic to confirm content is indeed not pinned\n    bool contentUnpinned = _verifyContentIsUnpinned(cid, node);\n    require(contentUnpinned, \"Content is still pinned\");\n\n    // Calculate remaining payment\n    uint256 remainingFunds = assignment.totalPayment - assignment.claimedAmount;\n\n    if (remainingFunds \u003e 0) {\n        // Return 95% to user\n        uint256 userRefund = (remainingFunds * 95) / 100;\n        (bool successUser, ) = assignment.user.call{value: userRefund}(\"\");\n        require(successUser, \"User refund failed\");\n\n        // Award 5% to reporter\n        uint256 reporterReward = remainingFunds - userRefund;\n        (bool successReporter, ) = payable(msg.sender).call{value: reporterReward}(\"\");\n        require(successReporter, \"Reporter reward failed\");\n\n        emit SlashingExecuted(assignmentId, node, userRefund, msg.sender, reporterReward);\n    }\n\n    // Mark assignment as inactive\n    assignment.active = false;\n\n    // Remove from node's active pins\n    nodes[node].activePins[cid] = false;\n\n    // Decrease node reputation\n    _decreaseReputation(node, SLASHING_REPUTATION_PENALTY);\n\n    // Check if ban is warranted\n    _evaluateBanning(node);\n}\n```\n\nIdeally, **verifiers** would interact with reputation-altering functions through an oracle or other interface that can atomically check IPFS pin status against contract storage. For the actual implementation, this would check IPFS network.\n\n```js\nfunction _verifyContentIsUnpinned(bytes32 cid, address node) internal returns (bool) {\n    //\n    // Returns true if content is confirmed to be unpinned\n    //\n    return true;\n}\n```\n\n## Challenges \u0026 Considerations 🏗️\n\nAs with any novel application there are a number of challenges and additional considerations that will need to be worked on as Pin.sol is developed. Some of these will have to be addressed before a mainnet launch, while others can be addressed later through community proposals.\n\nYou can find a non-exhaustive list of development challenges and considerations [here](CHALLENGES.md).\n\n## Community Collaboration 🤝\n\nThe goal is for Pin.sol to be run by stakeholders who would utilize the service as content providers, nodes, or verifiers. As such, we welcome contributions from:\n\n- 🔬 Researchers exploring storage incentivization models\n- 💻 Developers building on the protocol or creating verification tools\n- 🌐 IPFS node operators interested in monetizing their infrastructure\n- 🧪 Users with high-value storage needs seeking reliable solutions\n\n## Conclusion\n\nThis document outlines the vision for Pin.sol, an ETH-based decentralized storage incentivization layer for IPFS. The protocol leverages Ethereum's security, liquidity, and composability to create reliable distributed storage with fair access for all participating nodes.\n\nBy providing direct economic incentives for IPFS pinning through a balanced queue system, Pin.sol helps bridge the gap between decentralized storage technology and practical applications that require persistence guarantees.\n\n---\n\n### Contact\n\nIf you have any questions, open an issue or discussio thread, or reach out on [Warpcast](https://warpcast.com/jonbray.eth).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheyjonbray%2Fpindao","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheyjonbray%2Fpindao","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheyjonbray%2Fpindao/lists"}