From b60b763c8807eb4ac4c5488cbd759d6c4579d960 Mon Sep 17 00:00:00 2001 From: prityKushwaha Date: Mon, 21 Jul 2025 23:30:13 +0530 Subject: [PATCH 01/10] ether wallet contract --- EtherWallet/Etherwallet.sol | 17 ++++++++ EtherWallet/Readme.md | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 EtherWallet/Etherwallet.sol create mode 100644 EtherWallet/Readme.md diff --git a/EtherWallet/Etherwallet.sol b/EtherWallet/Etherwallet.sol new file mode 100644 index 0000000..b6ab105 --- /dev/null +++ b/EtherWallet/Etherwallet.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract EtherWallet { + address public owner = msg.sender; + + function deposit() public payable {} + + function withdraw(uint256 _amount) public { + require(msg.sender == owner, "Not owner"); + payable(owner).transfer(_amount); + } + + function getBalance() public view returns (uint256) { + return address(this).balance; + } +} diff --git a/EtherWallet/Readme.md b/EtherWallet/Readme.md new file mode 100644 index 0000000..12af2f0 --- /dev/null +++ b/EtherWallet/Readme.md @@ -0,0 +1,78 @@ +# EtherWallet + +## Contract Name +**EtherWallet** + +--- + +## Overview + +The `EtherWallet` smart contract provides a simple wallet mechanism on the Ethereum blockchain. It allows **any user to deposit Ether**, but **only the contract owner** (the deployer) can withdraw funds. This contract is useful for demonstrating Ethereum's basic smart contract functionality, fund handling, and ownership control. + +The contract is designed to be deployed and tested using the **QRemix IDE**, either with the **JavaScript VM** or the **Quranium Testnet**. + +--- + +## Prerequisites + +- QSafe (for testnet transactions) +- QRN (Quranium testnet tokens) +- Access to [QRemix IDE](https://qremix.org) +- Basic knowledge of Solidity and smart contract workflow + +--- + +## Contract Functions + +### `deposit() external payable` +- **Purpose:** Accepts Ether and stores it in the contract +- **Who Can Call:** Anyone +- **State Change:** Increases contract balance +- **Payable:** Yes + +--- + +### `withdraw(uint256 _amount) external` +- **Purpose:** Allows the contract owner to withdraw the specified amount of Ether +- **Who Can Call:** Only the owner (deployer) +- **Requires:** Sufficient balance and correct caller + +--- + +### `getBalance() external view returns (uint256)` +- **Purpose:** Returns the total Ether held in the contract +- **Who Can Call:** Anyone +- **View Function:** Does not modify state + +--- + +## Access Control + +- **Owner:** Set to `msg.sender` at deployment + - Can withdraw Ether using `withdraw()` +- **Public Users:** Can deposit Ether using `deposit()` + +Ownership logic is implemented directly using `msg.sender`. + +--- + +## Contract Code + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract EtherWallet { + address public owner = msg.sender; + + function deposit() public payable {} + + function withdraw(uint256 _amount) public { + require(msg.sender == owner, "Not owner"); + payable(owner).transfer(_amount); + } + + function getBalance() public view returns (uint256) { + return address(this).balance; + } +} From 778b2b19d11fc63910c0ef71bcc7930e9cfde527 Mon Sep 17 00:00:00 2001 From: prityKushwaha Date: Tue, 22 Jul 2025 11:13:19 +0530 Subject: [PATCH 02/10] readme update --- EtherWallet/Readme.md | 66 +++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/EtherWallet/Readme.md b/EtherWallet/Readme.md index 12af2f0..91ef994 100644 --- a/EtherWallet/Readme.md +++ b/EtherWallet/Readme.md @@ -13,15 +13,6 @@ The contract is designed to be deployed and tested using the **QRemix IDE**, eit --- -## Prerequisites - -- QSafe (for testnet transactions) -- QRN (Quranium testnet tokens) -- Access to [QRemix IDE](https://qremix.org) -- Basic knowledge of Solidity and smart contract workflow - ---- - ## Contract Functions ### `deposit() external payable` @@ -56,23 +47,50 @@ Ownership logic is implemented directly using `msg.sender`. --- -## Contract Code +## Deployment & Testing on QRemix + +### Step 1: Setup +- Open [qremix.org](https://qremix.org) +- Create folder: `Escrow/` +- Add `Escrow.sol` and paste the contract code + +### Step 2: Compile +- Go to Solidity Compiler +- Select version `0.8.20` +- Compile the contract + +### Step 3: Deploy + +#### Quranium Testnet +- Go to Deploy & Run Transactions +- Select Injected Provider - MetaMask +- Connect to Quranium testnet +- Deploy with ETH, passing the `_beneficiary`, `_arbiter`, and `_durationInSeconds` values -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +#### JavaScript VM +- Choose JavaScript VM +- Deploy locally with required constructor parameters and ETH + +### Step 4: Testing +- Deploy contract with ETH, beneficiary, arbiter, and time duration +- Call `markAsDelivered()` from the beneficiary address +- Call `releaseToBeneficiary()` from the depositor address +- Call `refundToDepositor()` from arbiter if delivery fails +- Call `resolveDispute(true/false)` from arbiter to test dispute resolution +- Use `claimAfterDeadline()` after 3 days past deadline (simulate using JavaScript VM) + +--- + +## License + +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the Solidity file. + +--- -contract EtherWallet { - address public owner = msg.sender; +## Support - function deposit() public payable {} +For issues or feature requests, refer to: +QRemix IDE Documentation : https://docs.qremix.org - function withdraw(uint256 _amount) public { - require(msg.sender == owner, "Not owner"); - payable(owner).transfer(_amount); - } - function getBalance() public view returns (uint256) { - return address(this).balance; - } -} From 878ab64108ecadf2aa2569d06f3a78a364af9015 Mon Sep 17 00:00:00 2001 From: prityKushwaha Date: Mon, 28 Jul 2025 21:42:21 +0530 Subject: [PATCH 03/10] contract added 28-7-25 --- SimpleEscrow/README.md | 100 +++++++++++++++++++++++++++++ SimpleEscrow/SimpleEscrow.sol | 28 +++++++++ SimpleVoting/README.md | 114 ++++++++++++++++++++++++++++++++++ SimpleVoting/SimpleVoting.sol | 48 ++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 SimpleEscrow/README.md create mode 100644 SimpleEscrow/SimpleEscrow.sol create mode 100644 SimpleVoting/README.md create mode 100644 SimpleVoting/SimpleVoting.sol diff --git a/SimpleEscrow/README.md b/SimpleEscrow/README.md new file mode 100644 index 0000000..8736086 --- /dev/null +++ b/SimpleEscrow/README.md @@ -0,0 +1,100 @@ +# SimpleEscrow + +## Contract Name +**SimpleEscrow** + +--- + +## Overview + +The `SimpleEscrow` smart contract holds Ether securely between a depositor and a beneficiary. Funds can only be released when an **arbiter** approves the transaction. This is ideal for use cases like **freelancing payments**, **purchase protection**, or **escrow-based trust models**. + +The contract is designed for easy deployment and testing on the **QRemix IDE** using the **JavaScript VM** or **Quranium Testnet**. + +--- + +## Contract Functions + +### `constructor(address _beneficiary, address _arbiter) payable` +- **Purpose:** Initializes the escrow with a beneficiary and arbiter. Ether must be sent during deployment. +- **Who Can Call:** Only once at deployment by depositor +- **Payable:** Yes +- **Sets:** + - `depositor` = `msg.sender` + - `beneficiary` = `_beneficiary` + - `arbiter` = `_arbiter` + +--- + +### `approve() external` +- **Purpose:** Releases the escrow funds to the beneficiary +- **Who Can Call:** Only the arbiter +- **Requires:** Must not already be approved +- **Effect:** Transfers contract balance to the beneficiary + +--- + +### `getBalance() external view returns (uint256)` +- **Purpose:** Returns current contract balance +- **Who Can Call:** Anyone +- **View Function:** Does not modify state + +--- + +## Access Control + +- **Depositor:** The person who deploys the contract with Ether +- **Beneficiary:** The intended recipient of the funds +- **Arbiter:** The neutral third-party responsible for approval +- **Approval Restriction:** Only arbiter can call `approve()` + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup +- Visit: [qremix.org](https://qremix.org) +- Create a new folder: `SimpleEscrow/` +- Add a new file `SimpleEscrow.sol` and paste the contract code + +### Step 2: Compile +- Go to **Solidity Compiler** +- Select version `0.8.20` +- Click **Compile SimpleEscrow.sol** + +### Step 3: Deploy + +#### Using Quranium Testnet +- Go to **Deploy & Run Transactions** +- Select `Injected Provider - MetaMask` +- Ensure Quranium testnet is selected in MetaMask +- Deploy with: + - `_beneficiary` address + - `_arbiter` address + - Send ETH with deployment (e.g., 0.1 ETH) + +#### Using JavaScript VM +- Choose `JavaScript VM` +- Deploy locally with valid `_beneficiary`, `_arbiter`, and value + +### Step 4: Testing +- **Approve Funds:** + Call `approve()` using the arbiter address +- **View Balance:** + Call `getBalance()` anytime to check contract ETH +- **Check Restrictions:** + Try calling `approve()` from other accounts—it should fail + +--- + +## License + +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the Solidity file. + +--- + +## Support + +For help and documentation, refer to: +📘 QRemix IDE: https://docs.qremix.org diff --git a/SimpleEscrow/SimpleEscrow.sol b/SimpleEscrow/SimpleEscrow.sol new file mode 100644 index 0000000..4c2d2e8 --- /dev/null +++ b/SimpleEscrow/SimpleEscrow.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract SimpleEscrow { + address public depositor; + address public beneficiary; + address public arbiter; + bool public isApproved; + + constructor(address _beneficiary, address _arbiter) payable { + depositor = msg.sender; + beneficiary = _beneficiary; + arbiter = _arbiter; + isApproved = false; + } + + function approve() external { + require(msg.sender == arbiter, "Only arbiter can approve"); + require(!isApproved, "Already approved"); + + isApproved = true; + payable(beneficiary).transfer(address(this).balance); + } + + function getBalance() external view returns (uint256) { + return address(this).balance; + } +} diff --git a/SimpleVoting/README.md b/SimpleVoting/README.md new file mode 100644 index 0000000..c9522b5 --- /dev/null +++ b/SimpleVoting/README.md @@ -0,0 +1,114 @@ +# SimpleVoting + +## Contract Name +**SimpleVoting** + +--- + +## Overview + +The `SimpleVoting` smart contract allows a set of candidates to receive votes from Ethereum users. Each address can **vote only once**, and the voting can be **ended by the contract owner**. + +This contract is useful for demonstrating **decentralized governance**, **polling**, or **community decision-making** on the blockchain. + +--- + +## Contract Functions + +### `constructor(string[] memory _candidates)` +- **Purpose:** Initializes the list of candidates and sets the deployer as the owner +- **Who Can Call:** Only during deployment +- **Sets:** + - `owner` = `msg.sender` + - `candidates` = `_candidates` + - `votingActive` = `true` + +--- + +### `vote(string memory _candidate) external` +- **Purpose:** Casts a vote for the given candidate +- **Who Can Call:** Any address (once only) +- **Requires:** + - Voting must be active + - Caller has not voted before + - Candidate must be valid + +--- + +### `endVoting() external` +- **Purpose:** Ends the voting session +- **Who Can Call:** Only the owner +- **Effect:** Sets `votingActive` to `false` + +--- + +### `getVotes(string memory _candidate) external view returns (uint256)` +- **Purpose:** Gets the vote count for a specific candidate +- **Who Can Call:** Anyone +- **View Function:** Read-only + +--- + +### `getAllCandidates() external view returns (string[] memory)` +- **Purpose:** Returns the list of all candidates +- **Who Can Call:** Anyone +- **View Function:** Read-only + +--- + +## Access Control + +- **Owner:** + - Set to the deploying address + - Can end the voting session using `endVoting()` + +- **Voters:** + - Any address can vote once using `vote()` + - Voting is disabled once ended + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup +- Open [qremix.org](https://qremix.org) +- Create a new folder: `VotingSystem/` +- Add a new file: `SimpleVoting.sol` and paste the code + +### Step 2: Compile +- Go to **Solidity Compiler** +- Select version `0.8.20` +- Click **Compile SimpleVoting.sol** + +### Step 3: Deploy + +#### Using JavaScript VM +- Go to **Deploy & Run Transactions** +- Select `JavaScript VM` +- Input constructor parameters like: `["Alice", "Bob", "Charlie"]` +- Click **Deploy** + +#### Using Quranium Testnet +- Connect MetaMask to Quranium testnet +- Select `Injected Provider - MetaMask` +- Deploy contract with candidate array as constructor input + +### Step 4: Testing +- Call `vote("Alice")` from different accounts +- Call `getVotes("Alice")` to verify count +- Call `endVoting()` from owner account +- Try voting again — should be rejected + +--- + +## License + +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the Solidity file. + +--- + +## Support + +For issues or feature requests, refer to: +📘 QRemix IDE Documentation: https://docs.qremix.org diff --git a/SimpleVoting/SimpleVoting.sol b/SimpleVoting/SimpleVoting.sol new file mode 100644 index 0000000..a81475d --- /dev/null +++ b/SimpleVoting/SimpleVoting.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract SimpleVoting { + address public owner; + string[] public candidates; + mapping(string => uint256) public votes; + mapping(address => bool) public hasVoted; + bool public votingActive; + + constructor(string[] memory _candidates) { + owner = msg.sender; + candidates = _candidates; + votingActive = true; + } + + function vote(string memory _candidate) external { + require(votingActive, "Voting is not active"); + require(!hasVoted[msg.sender], "You have already voted"); + + bool valid = false; + for (uint i = 0; i < candidates.length; i++) { + if ( + keccak256(bytes(candidates[i])) == keccak256(bytes(_candidate)) + ) { + valid = true; + break; + } + } + + require(valid, "Invalid candidate"); + votes[_candidate]++; + hasVoted[msg.sender] = true; + } + + function endVoting() external { + require(msg.sender == owner, "Only owner can end voting"); + votingActive = false; + } + + function getVotes(string memory _candidate) external view returns (uint256) { + return votes[_candidate]; + } + + function getAllCandidates() external view returns (string[] memory) { + return candidates; + } +} From aa2382c14de3e1267655b5f13083945ede48d42f Mon Sep 17 00:00:00 2001 From: Ayan Date: Mon, 28 Jul 2025 23:44:50 +0530 Subject: [PATCH 04/10] add detailed README.md for TimeForge proof-of-time NFT contract --- TimeForge/README.md | 137 ++++++++++++++++++++++++++++++++++++++++ TimeForge/TimeForge.sol | 78 +++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 TimeForge/README.md create mode 100644 TimeForge/TimeForge.sol diff --git a/TimeForge/README.md b/TimeForge/README.md new file mode 100644 index 0000000..dfc87f8 --- /dev/null +++ b/TimeForge/README.md @@ -0,0 +1,137 @@ +# TimeForge + +#### Smart Contract: Proof-of-Time Commitment NFT + +--- + +## Overview + +**TimeForge** is a gamified smart contract system that rewards users with a unique NFT for successfully completing a time-based challenge. Participants must check in daily over a set number of days. If completed without missing a check-in, the user receives a non-transferable NFT as proof of dedication. + +Ideal for: +- Fitness or wellness challenges (e.g., 30-day yoga) +- Learning milestones (e.g., daily coding) +- Habit tracking (e.g., meditation, journaling) +- DAO work consistency rewards + +> Fully compatible with **OpenZeppelin** libraries and deployable via **QRemix IDE**, JavaScript VM, or testnets like **Quranium** or **Sepolia**. + +--- + +## Prerequisites + +To deploy and test the contract, you’ll need: + +- **MetaMask or QSafe**: Wallet extension for testnet deployment +- **Test ETH or QRN**: Get from [sepoliafaucet.com](https://sepoliafaucet.com) or [faucet.quranium.org](https://faucet.quranium.org) +- **QRemix IDE**: Visit [qremix.org](https://qremix.org) +- **Solidity Knowledge**: Understanding of smart contracts and NFT standards + +--- + +## Contract Details + +### Structs + +#### `Challenge` +- `user`: Participant address +- `startTime`: Timestamp when challenge started +- `durationDays`: Total days required +- `lastCheckIn`: Timestamp of last check-in +- `checkIns`: Number of completed check-ins +- `completed`: True if challenge was successful +- `failed`: True if challenge failed + +--- + +### Functions + +#### `startChallenge(uint256 durationDays)` +- **Purpose**: Begins a new challenge for the sender +- **Parameters**: + - `durationDays`: Total number of days to complete +- **Access**: Anyone (one challenge per address) + +#### `checkIn()` +- **Purpose**: Perform a daily check-in +- **Rules**: + - Must be at least 23 hours after previous check-in + - Automatically completes challenge after final check-in + +#### `failChallenge()` +- **Purpose**: Allows user or external app to mark challenge as failed +- **Rules**: Can be failed if >48 hours since last check-in + +#### `getChallengeStatus(address user)` +- **Returns**: "In Progress", "Completed", or "Failed" + +--- + +### 🖼 NFT Reward + +- Minted using `_safeMint()` when challenge is completed +- Uses `_setTokenURI()` for attaching metadata +- NFT serves as verifiable, non-transferable proof of consistency + +--- + +## Deployment and Testing in QRemix IDE + +### Step 1: Setup +1. Visit [qremix.org](https://qremix.org) +2. Create folder: `TimeForge/` +3. Create file: `TimeForge.sol` +4. Paste the smart contract code + +### Step 2: Compilation +1. Go to **Solidity Compiler** +2. Select version `^0.8.20` or above +3. Compile `TimeForge.sol` + +### Step 3: Deployment + +#### JavaScript VM (Local Testing) +1. Go to **Deploy & Run Transactions** +2. Select environment: `JavaScript VM` +3. Deploy the `TimeForge` contract + +#### Quranium or Sepolia Testnet +1. Select environment: `Injected Provider - MetaMask` +2. Ensure MetaMask is connected to the correct testnet +3. Deploy the contract + +--- + +## Testing Instructions + +### Start and Complete a Challenge +1. Call `startChallenge` with e.g. `7` for 7 days +2. Call `checkIn()` once per day (every 23+ hrs) +3. After final check-in, NFT is automatically minted + +### Challenge Status +1. Use `getChallengeStatus(address)` to monitor state +2. Returns: + - `"In Progress"` during challenge + - `"Completed"` after all check-ins + - `"Failed"` if check-ins missed + +### Failure Conditions +1. Call `failChallenge()` if >48 hrs passed since last check-in +2. `checkIn()` or reward is blocked if challenge fails + +--- + +## License + +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the Solidity file. + +--- + +## Support + +For questions, feedback, or bug reports: +- Visit the [QRemix IDE Documentation](https://docs.qremix.org) +- Refer to [OpenZeppelin Docs](https://docs.openzeppelin.com/contracts) + diff --git a/TimeForge/TimeForge.sol b/TimeForge/TimeForge.sol new file mode 100644 index 0000000..66421d9 --- /dev/null +++ b/TimeForge/TimeForge.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract TimeForge is ERC721URIStorage, Ownable { + uint256 public nextTokenId; + + struct Challenge { + address user; + uint256 startTime; + uint256 durationDays; + uint256 lastCheckIn; + uint256 checkIns; + bool completed; + bool failed; + } + + mapping(address => Challenge) public challenges; + + constructor() ERC721("TimeForgeNFT", "TFN") {} + + modifier onlyActiveChallenge() { + require(challenges[msg.sender].user != address(0), "No active challenge"); + require(!challenges[msg.sender].completed, "Challenge already completed"); + require(!challenges[msg.sender].failed, "Challenge failed"); + _; + } + + function startChallenge(uint256 _durationDays) external { + require(challenges[msg.sender].user == address(0), "Challenge already started"); + + challenges[msg.sender] = Challenge({ + user: msg.sender, + startTime: block.timestamp, + durationDays: _durationDays, + lastCheckIn: block.timestamp, + checkIns: 0, + completed: false, + failed: false + }); + } + + function checkIn() external onlyActiveChallenge { + Challenge storage c = challenges[msg.sender]; + require(block.timestamp >= c.lastCheckIn + 23 hours, "Check-in too soon"); + require(block.timestamp <= c.startTime + c.durationDays * 1 days, "Challenge expired"); + + c.checkIns += 1; + c.lastCheckIn = block.timestamp; + + if (c.checkIns >= c.durationDays) { + c.completed = true; + _mintNFT(msg.sender); + } + } + + function failChallenge() external onlyActiveChallenge { + Challenge storage c = challenges[msg.sender]; + if (block.timestamp > c.lastCheckIn + 48 hours) { + c.failed = true; + } + } + + function _mintNFT(address to) internal { + uint256 tokenId = nextTokenId++; + _safeMint(to, tokenId); + _setTokenURI(tokenId, "ipfs://your_generated_metadata"); // Link to badge + } + + function getChallengeStatus(address user) public view returns (string memory) { + Challenge memory c = challenges[user]; + if (c.failed) return "Failed"; + if (c.completed) return "Completed"; + return "In Progress"; + } +} From 8bbe8fde1bd3ce84450c9b85c65319b75b666411 Mon Sep 17 00:00:00 2001 From: ronit Date: Tue, 26 Aug 2025 11:40:15 +0530 Subject: [PATCH 05/10] AmazonListing and Insurance claim --- AmazonListing/MarketPlace.sol | 261 ++++++++++++++++++++++++++++++++++ AmazonListing/Readme.md | 211 +++++++++++++++++++++++++++ Insurance/Readme.md | 132 +++++++++++++++++ Insurance/SimpleInsurance.sol | 43 ++++++ 4 files changed, 647 insertions(+) create mode 100644 AmazonListing/MarketPlace.sol create mode 100644 AmazonListing/Readme.md create mode 100644 Insurance/Readme.md create mode 100644 Insurance/SimpleInsurance.sol diff --git a/AmazonListing/MarketPlace.sol b/AmazonListing/MarketPlace.sol new file mode 100644 index 0000000..090a644 --- /dev/null +++ b/AmazonListing/MarketPlace.sol @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract MarketPlace is ReentrancyGuard, Ownable { + uint public productCount; + uint public purchaseCount; + + enum PurchaseStatus { Locked, Delivered, Released, Refunded, Disputed, Resolved } + + struct Product { + uint id; + string name; + string description; + uint price; // price per unit (wei) + uint stock; + address payable seller; + bool exists; + } + + struct Purchase { + uint id; + uint productId; + address payable buyer; + uint quantity; + uint totalPrice; + PurchaseStatus status; + uint createdAt; + } + + struct Review { + address reviewer; + uint8 rating; // 1-5 + string comment; + uint timestamp; + } + + // productId => Product + mapping(uint => Product) public products; + + // purchaseId => Purchase + mapping(uint => Purchase) public purchases; + + // productId => list of reviews + mapping(uint => Review[]) internal productReviews; + + // purchaseId => whether review left + mapping(uint => bool) public purchaseReviewed; + + // seller => pendingWithdrawals (in case you want to store rather than auto-transfer) + mapping(address => uint) public pendingWithdrawals; // (not currently required, kept optional) + + /* -------------------- Events -------------------- */ + event ProductListed(uint indexed productId, address indexed seller, string name, uint price, uint stock); + event ProductUpdated(uint indexed productId, uint price, uint stock); + event ProductPurchased(uint indexed purchaseId, uint indexed productId, address indexed buyer, uint quantity, uint totalPrice); + event DeliveryConfirmed(uint indexed purchaseId); + event FundsReleased(uint indexed purchaseId, address indexed seller, uint amount); + event RefundIssued(uint indexed purchaseId, address indexed buyer, uint amount); + event DisputeOpened(uint indexed purchaseId, address indexed opener, string reason); + event DisputeResolved(uint indexed purchaseId, address indexed resolver, bool releasedToSeller, uint sellerAmount, uint buyerAmount); + event ReviewLeft(uint indexed productId, address indexed reviewer, uint8 rating, string comment); + event ProductRemoved(uint indexed productId); + + /* -------------------- Modifiers -------------------- */ + + modifier productExists(uint productId) { + require(products[productId].exists, "product not found"); + _; + } + + modifier onlyBuyer(uint purchaseId) { + require(purchases[purchaseId].buyer == msg.sender, "not buyer of purchase"); + _; + + } + //Seller specific functions + function listProduct( + string calldata name, + string calldata description, + uint price, + uint stock + ) external returns (uint) { + require(price > 0, "price>0"); + require(stock > 0, "stock>0"); + + productCount++; + products[productCount] = Product({ + id: productCount, + name: name, + description: description, + price: price, + stock: stock, + seller: payable(msg.sender), + exists: true + }); + + emit ProductListed(productCount, msg.sender, name, price, stock); + return productCount; + } + + function updateProduct(uint productId, uint newPrice, uint newStock) external productExists(productId) { + Product storage p = products[productId]; + require(p.seller == msg.sender, "not seller"); + require(newPrice > 0, "price>0"); + p.price = newPrice; + p.stock = newStock; + emit ProductUpdated(productId, newPrice, newStock); + } + + function removeProduct(uint productId) external productExists(productId) { + Product storage p = products[productId]; + require(p.seller == msg.sender || owner() == msg.sender, "not authorized"); + delete products[productId]; + emit ProductRemoved(productId); + } + + //buyer specific functions + function buyProduct(uint productId, uint quantity) external payable nonReentrant productExists(productId) returns (uint) { + require(quantity > 0, "quantity>0"); + Product storage p = products[productId]; + require(p.stock >= quantity, "not enough stock"); + + uint total = p.price * quantity; + require(msg.value == total, "incorrect payment"); + + // reduce stock immediately + p.stock -= quantity; + + purchaseCount++; + purchases[purchaseCount] = Purchase({ + id: purchaseCount, + productId: productId, + buyer: payable(msg.sender), + quantity: quantity, + totalPrice: total, + status: PurchaseStatus.Locked, + createdAt: block.timestamp + }); + + emit ProductPurchased(purchaseCount, productId, msg.sender, quantity, total); + return purchaseCount; + } + + /// @notice Buyer confirms delivery; releases funds to seller + function confirmDelivery(uint purchaseId) external nonReentrant onlyBuyer(purchaseId) { + Purchase storage pur = purchases[purchaseId]; + require(pur.status == PurchaseStatus.Locked || pur.status == PurchaseStatus.Delivered, "cannot confirm"); + pur.status = PurchaseStatus.Released; + + Product storage prod = products[pur.productId]; + uint amount = pur.totalPrice; + + // transfer ETH to seller + (bool ok, ) = prod.seller.call{value: amount}(""); + require(ok, "transfer failed"); + + emit DeliveryConfirmed(purchaseId); + emit FundsReleased(purchaseId, prod.seller, amount); + } + + + /// @notice Buyer or seller open a dispute + function openDispute(uint purchaseId, string calldata reason) external { + Purchase storage pur = purchases[purchaseId]; + require(pur.status == PurchaseStatus.Locked || pur.status == PurchaseStatus.Delivered, "cannot dispute"); + Product storage prod = products[pur.productId]; + require(msg.sender == pur.buyer || msg.sender == prod.seller, "not party"); + + pur.status = PurchaseStatus.Disputed; + emit DisputeOpened(purchaseId, msg.sender, reason); + } + + /// @notice Owner (or arbitrator owner) resolves dispute. Provide amounts to buyer and seller (sum must equal totalPrice) + function resolveDispute(uint purchaseId, address payable toSeller, address payable toBuyer, uint sellerAmount, uint buyerAmount) external onlyOwner nonReentrant { + Purchase storage pur = purchases[purchaseId]; + require(pur.status == PurchaseStatus.Disputed, "not disputed"); + require(sellerAmount + buyerAmount == pur.totalPrice, "amount mismatch"); + + pur.status = PurchaseStatus.Resolved; + + // transfer to seller if any + if (sellerAmount > 0) { + (bool ok1, ) = toSeller.call{value: sellerAmount}(""); + require(ok1, "seller transfer failed"); + } + + // transfer to buyer if any (refund) + if (buyerAmount > 0) { + (bool ok2, ) = toBuyer.call{value: buyerAmount}(""); + require(ok2, "buyer refund failed"); + } + + emit DisputeResolved(purchaseId, msg.sender, sellerAmount > 0, sellerAmount, buyerAmount); + } + + /// @notice Leave a review for the purchased product. Can only be called by the buyer after purchase is released or resolved in buyer's favor. + function leaveReview(uint purchaseId, uint8 rating, string calldata comment) external { + require(rating >= 1 && rating <= 5, "rating 1-5"); + Purchase storage pur = purchases[purchaseId]; + + require(pur.buyer == msg.sender, "not buyer"); + require(!purchaseReviewed[purchaseId], "already reviewed"); + require( + pur.status == PurchaseStatus.Released || + pur.status == PurchaseStatus.Resolved || + pur.status == PurchaseStatus.Refunded, + "cannot review yet" + ); + + productReviews[pur.productId].push(Review({ + reviewer: msg.sender, + rating: rating, + comment: comment, + timestamp: block.timestamp + })); + + purchaseReviewed[purchaseId] = true; + emit ReviewLeft(pur.productId, msg.sender, rating, comment); + } + + function getProduct(uint productId) external view productExists(productId) returns ( + uint id, + string memory name, + string memory description, + uint price, + uint stock, + address seller + ) { + Product storage p = products[productId]; + return (p.id, p.name, p.description, p.price, p.stock, p.seller); + } + + function getPurchase(uint purchaseId) external view returns ( + uint id, + uint productId, + address buyer, + uint quantity, + uint totalPrice, + PurchaseStatus status, + uint createdAt + ) { + Purchase storage pur = purchases[purchaseId]; + return (pur.id, pur.productId, pur.buyer, pur.quantity, pur.totalPrice, pur.status, pur.createdAt); + } + + function getReviews(uint productId) external view returns (Review[] memory) { + return productReviews[productId]; + } + // Allow owner to withdraw accidentally stuck funds (emergency) + function emergencyWithdraw(address payable to, uint amount) external onlyOwner nonReentrant { + require(address(this).balance >= amount, "insufficient balance"); + (bool ok, ) = to.call{value: amount}(""); + require(ok, "withdraw failed"); + } + + // Fallback to accept ETH (e.g., accidental sends) + receive() external payable {} +} diff --git a/AmazonListing/Readme.md b/AmazonListing/Readme.md new file mode 100644 index 0000000..ab171d9 --- /dev/null +++ b/AmazonListing/Readme.md @@ -0,0 +1,211 @@ +# MarketPlace + +## Contract Name +**MarketPlace** + +--- + +## Overview + +The `MarketPlace` contract implements a simple Amazon-style marketplace with built-in escrow, dispute resolution, and a ratings & reviews system. Sellers list products with price and stock. Buyers purchase items and the payment is held in escrow until the buyer confirms delivery. If a dispute arises, the contract owner (arbitrator) can resolve the dispute and distribute funds accordingly. + +This contract is ideal for prototyping e-commerce marketplaces, learning escrow flows, or building a minimal decentralized marketplace. It is designed to be deployed and tested in the QRemix IDE using the JavaScript VM or a testnet. + +--- + +## Prerequisites + +- MetaMask or QSafe (for testnet deployment) +- Testnet ETH or QRN +- Access to QRemix IDE (or any Solidity dev environment) +- Basic knowledge of Solidity and smart contract workflow + +--- + +## Contract Functions + +### Constructor + +```solidity +constructor() +``` +- **Purpose:** Default Ownable constructor (owner = deployer). +- **Access:** Called on deployment. + +--- + +### listProduct + +```solidity +listProduct(string name, string description, uint price, uint stock) → uint +``` +- **Purpose:** Seller lists a new product with price and stock. +- **Access:** Any address (becomes seller). +- **Returns:** productId (uint) + +--- + +### updateProduct + +```solidity +updateProduct(uint productId, uint newPrice, uint newStock) +``` +- **Purpose:** Seller updates price and stock for their product. +- **Access:** Only product seller. + +--- + +### removeProduct + +```solidity +removeProduct(uint productId) +``` +- **Purpose:** Remove a product listing. Seller or contract owner can remove. +- **Access:** Seller or owner. + +--- + +### buyProduct + +```solidity +buyProduct(uint productId, uint quantity) payable → uint +``` +- **Purpose:** Buyer purchases quantity of a product. Funds are held in contract (escrow). +- **Conditions:** `msg.value == price * quantity` and `stock >= quantity`. +- **Returns:** purchaseId (uint) + +--- + +### confirmDelivery + +```solidity +confirmDelivery(uint purchaseId) +``` +- **Purpose:** Buyer confirms delivery — contract releases funds to the seller. +- **Access:** Only the buyer of that purchase. + +--- + +### openDispute + +```solidity +openDispute(uint purchaseId, string reason) +``` +- **Purpose:** Buyer or seller flags the purchase as disputed. Funds remain in contract. +- **Access:** Buyer or seller. + +--- + +### resolveDispute + +```solidity +resolveDispute(uint purchaseId, address payable toSeller, address payable toBuyer, uint sellerAmount, uint buyerAmount) +``` +- **Purpose:** Owner (arbitrator) resolves a dispute by distributing the escrowed total between seller and buyer. +- **Access:** Only contract owner. +- **Condition:** `sellerAmount + buyerAmount == totalPrice`. + +--- + +### leaveReview + +```solidity +leaveReview(uint purchaseId, uint8 rating, string comment) +``` +- **Purpose:** Buyer leaves a 1–5 rating and comment for the product after purchase is completed/refunded/resolved. One review per purchase. +- **Access:** Only the buyer of that purchase; purchase must be in an allowed status. + +--- + +### getProduct + +```solidity +getProduct(uint productId) → (id, name, description, price, stock, seller) +``` +- **Purpose:** Fetch product metadata and seller address. + +--- + +### getPurchase + +```solidity +getPurchase(uint purchaseId) → (id, productId, buyer, quantity, totalPrice, status, createdAt) +``` +- **Purpose:** Fetch purchase details and escrow status. + +--- + +### getReviews + +```solidity +getReviews(uint productId) → Review[] +``` +- **Purpose:** Returns the list of reviews for a product. Each review contains: reviewer, rating (1–5), comment, and timestamp. + +--- + +## Access Control + +- **Owner (Deployer):** Arbitration power (resolve disputes), emergency withdrawal, and can remove product listings. +- **Sellers:** Can list, update, and remove their products. +- **Buyers:** Can buy products, confirm delivery, open disputes for their purchases, and leave reviews once eligible. + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup +- Open [qremix.org](https://qremix.org) +- Create folder: `MarketPlace/` +- Add `MarketPlace.sol` and paste the contract code. + +### Step 2: Compile +- Go to Solidity Compiler +- Select version `0.8.20` (or compatible `^0.8.20`) +- Compile the contract + +### Step 3: Deploy + +#### Quranium Testnet +- Go to Deploy & Run Transactions +- Select Injected Provider - MetaMask +- Connect to Quranium testnet +- Deploy (no constructor args) + +#### JavaScript VM +- Choose JavaScript VM +- Deploy directly with the default admin (deployer will be owner) + +### Step 4: Testing Flow + +1. **Seller:** Call `listProduct("Phone", "Nice phone", , 10)`. Note the returned productId. +2. **Buyer:** Call `buyProduct(productId, quantity)` sending `price * quantity` wei. Note purchaseId. +3. **Buyer:** After receiving item (simulate), call `confirmDelivery(purchaseId)` to release funds to seller. +4. **If problem:** Either party calls `openDispute(purchaseId, "reason")`. +5. **Owner:** Call `resolveDispute(purchaseId, sellerAddress, buyerAddress, sellerAmount, buyerAmount)` distributing the escrowed funds. +6. **Buyer:** After Released or Resolved, call `leaveReview(purchaseId, rating, "comment")`. + +--- + +## Security Notes & Recommendations + +- The contract uses `nonReentrant` for functions handling ETH transfers. +- Currently uses ETH-only flow. For token support (ERC20), integrate SafeERC20 and hold token balances in escrow. +- Arbitration authority is the contract owner — for production, replace owner-based arbitration with a DAO, multisig, or a decentralized arbitration protocol. +- Add time-based auto-release (seller can request release after X days) and dispute timeouts as enhancements. +- Always test edge cases: partial refunds, failed transfers, reentrancy attempts, and inconsistent states. + +--- + +## License + +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the Solidity file + +--- + +## Support +For issues or feature requests, refer to: +[QRemix IDE Documentation](https://docs.qremix.org) + +--- \ No newline at end of file diff --git a/Insurance/Readme.md b/Insurance/Readme.md new file mode 100644 index 0000000..0289d9f --- /dev/null +++ b/Insurance/Readme.md @@ -0,0 +1,132 @@ +# SimpleInsurance + +## Contract Name +**SimpleInsurance** + +--- + +## Overview + +The `SimpleInsurance` contract provides a basic insurance model where customers can purchase coverage by paying a fixed premium. Once insured, a customer can make a claim, and the contract will automatically pay out the agreed compensation if sufficient funds are available. + +The insurer (contract deployer) can fund the contract to ensure liquidity for payouts. This design makes it ideal for learning, prototyping, and demonstrating decentralized insurance systems in a simplified way. + +It is designed to be deployed and tested in the QRemix IDE using the JavaScript VM or a testnet. + +--- + +## Prerequisites + +- MetaMask or QSafe (for testnet deployment) +- Testnet ETH or QRN +- Access to QRemix IDE +- Basic knowledge of Solidity and smart contract workflow + +--- + +## Contract Functions + +### Constructor + +```solidity +constructor(uint _premium, uint _payoutAmount) +``` +- **Purpose:** Initializes the premium (cost of insurance) and payout amount. Sets the deployer as the insurer. +- **Access:** Called on deployment + +--- + +### buyInsurance + +```solidity +buyInsurance() external payable +``` +- **Purpose:** Allows a customer to buy insurance by paying the exact premium. +- **Condition:** `msg.value` must equal the premium. +- **Effect:** Marks the customer as insured. + +--- + +### makeClaim + +```solidity +makeClaim() external +``` +- **Purpose:** Lets an insured customer claim their payout. +- **Conditions:** + - Caller must be insured. + - Claim must not have been made already. +- **Effect:** Transfers the payout amount to the claimant. + +--- + +### fundContract + +```solidity +fundContract() external payable +``` +- **Purpose:** Allows the insurer to add funds to the contract to cover future claims. +- **Access:** Only the insurer can call this function. + +--- + +### getBalance + +```solidity +getBalance() external view returns (uint) +``` +- **Purpose:** Returns the total ETH balance held by the contract. + +--- + +## Access Control + +- **Insurer (Deployer):** Deploys, funds the contract, and defines premium/payout amounts. +- **Customers:** Can buy insurance and make claims if insured. + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup +- Open [qremix.org](https://qremix.org) +- Create folder: `SimpleInsurance/` +- Add `SimpleInsurance.sol` and paste the contract code + +### Step 2: Compile +- Go to Solidity Compiler +- Select version `0.8.20` +- Compile the contract + +### Step 3: Deploy + +#### Quranium Testnet +- Go to Deploy & Run Transactions +- Select Injected Provider - MetaMask +- Connect to Quranium testnet +- Deploy with constructor arguments: + - `_premium` → e.g., `1000000000000000000` (1 ETH in wei) + - `_payoutAmount` → e.g., `5000000000000000000` (5 ETH in wei) + +#### JavaScript VM +- Choose JavaScript VM +- Deploy directly with constructor arguments (premium & payout). + +### Step 4: Testing +- Call `buyInsurance()` with exact ETH equal to premium. +- Call `makeClaim()` from the insured address to get payout. +- Insurer can call `fundContract()` to add liquidity. +- Use `getBalance()` to check available contract funds. + +--- + +## License + +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the Solidity file. + +--- + +## Support +For issues or feature requests, refer to: +[QRemix IDE Documentation](https://docs.qremix.org) \ No newline at end of file diff --git a/Insurance/SimpleInsurance.sol b/Insurance/SimpleInsurance.sol new file mode 100644 index 0000000..0e388f4 --- /dev/null +++ b/Insurance/SimpleInsurance.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SimpleInsurance { + address public insurer; // The insurance company + uint public premium; // Fixed premium amount + uint public payoutAmount; // Amount insurer pays out in case of claim + bool public claimMade; // To track if a claim is already made + + mapping(address => bool) public insured; + + constructor(uint _premium, uint _payoutAmount) { + insurer = msg.sender; + premium = _premium; + payoutAmount = _payoutAmount; + claimMade = false; + } + + // Customer buys insurance by paying the premium + function buyInsurance() external payable { + require(msg.value == premium, "Must pay exact premium amount"); + insured[msg.sender] = true; + } + + // Customer makes a claim + function makeClaim() external { + require(insured[msg.sender], "You are not insured"); + require(!claimMade, "Claim already made"); + + claimMade = true; + payable(msg.sender).transfer(payoutAmount); + } + + // Allow insurer to fund the contract (to cover payouts) + function fundContract() external payable { + require(msg.sender == insurer, "Only insurer can fund"); + } + + // Check contract balance + function getBalance() external view returns (uint) { + return address(this).balance; + } +} From 54f6002ae9d5a4a82801dab4ad8e5ca9f49b9823 Mon Sep 17 00:00:00 2001 From: Ronit Kumar <113817046+ronitkumar98@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:43:15 +0530 Subject: [PATCH 06/10] Update Readme.md --- Insurance/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Insurance/Readme.md b/Insurance/Readme.md index 0289d9f..6f4e093 100644 --- a/Insurance/Readme.md +++ b/Insurance/Readme.md @@ -108,8 +108,8 @@ getBalance() external view returns (uint) - `_premium` → e.g., `1000000000000000000` (1 ETH in wei) - `_payoutAmount` → e.g., `5000000000000000000` (5 ETH in wei) -#### JavaScript VM -- Choose JavaScript VM +#### QRemix VM +- Choose QRemix VM - Deploy directly with constructor arguments (premium & payout). ### Step 4: Testing @@ -129,4 +129,4 @@ See `SPDX-License-Identifier: MIT` in the Solidity file. ## Support For issues or feature requests, refer to: -[QRemix IDE Documentation](https://docs.qremix.org) \ No newline at end of file +[QRemix IDE Documentation](https://docs.qremix.org) From 68bae1b4fab6fc4658a8b96625176cc1638f5072 Mon Sep 17 00:00:00 2001 From: Ronit Kumar <113817046+ronitkumar98@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:44:10 +0530 Subject: [PATCH 07/10] Update Readme.md --- AmazonListing/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AmazonListing/Readme.md b/AmazonListing/Readme.md index ab171d9..3e6f458 100644 --- a/AmazonListing/Readme.md +++ b/AmazonListing/Readme.md @@ -172,8 +172,8 @@ getReviews(uint productId) → Review[] - Connect to Quranium testnet - Deploy (no constructor args) -#### JavaScript VM -- Choose JavaScript VM +#### QRemix VM +- Choose QRemix VM - Deploy directly with the default admin (deployer will be owner) ### Step 4: Testing Flow @@ -208,4 +208,4 @@ See `SPDX-License-Identifier: MIT` in the Solidity file For issues or feature requests, refer to: [QRemix IDE Documentation](https://docs.qremix.org) ---- \ No newline at end of file +--- From 83e1f5447d438ac9b45ced2166dd095fdfc35875 Mon Sep 17 00:00:00 2001 From: isha rathod Date: Sat, 30 Aug 2025 20:35:08 +0530 Subject: [PATCH 08/10] Added 5 new contracts --- DynamicRoyalty/DynamicRoyalty.sol | 62 +++++++++ DynamicRoyalty/Readme.md | 168 ++++++++++++++++++++++ Escrow/escrow.sol | 58 ++++++++ Escrow/readme.md | 218 +++++++++++++++++++++++++++++ LendingNFT.sol/Lending.sol | 65 +++++++++ LendingNFT.sol/Readme.md | 200 +++++++++++++++++++++++++++ ReputationSystem/Readme.md | 190 +++++++++++++++++++++++++ ReputationSystem/Reputation.sol | 31 +++++ SubscriptionAccess.sol/Access.sol | 48 +++++++ SubscriptionAccess.sol/readme.md | 223 ++++++++++++++++++++++++++++++ 10 files changed, 1263 insertions(+) create mode 100644 DynamicRoyalty/DynamicRoyalty.sol create mode 100644 DynamicRoyalty/Readme.md create mode 100644 Escrow/escrow.sol create mode 100644 Escrow/readme.md create mode 100644 LendingNFT.sol/Lending.sol create mode 100644 LendingNFT.sol/Readme.md create mode 100644 ReputationSystem/Readme.md create mode 100644 ReputationSystem/Reputation.sol create mode 100644 SubscriptionAccess.sol/Access.sol create mode 100644 SubscriptionAccess.sol/readme.md diff --git a/DynamicRoyalty/DynamicRoyalty.sol b/DynamicRoyalty/DynamicRoyalty.sol new file mode 100644 index 0000000..fec353d --- /dev/null +++ b/DynamicRoyalty/DynamicRoyalty.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IERC721Token { + function transferFrom(address from, address to, uint256 tokenId) external; +} + +contract RoyaltyMarketplace { + struct Listing { + address seller; + address nft; + uint256 tokenId; + uint256 price; + address[] collaborators; + uint256[] shares; + } + + uint256 public listingCount; + mapping(uint256 => Listing) public listings; + + event Listed(uint256 id, address seller, uint256 price); + event Purchased(uint256 id, address buyer); + + function listNFT( + address _nft, + uint256 _tokenId, + uint256 _price, + address[] memory _collaborators, + uint256[] memory _shares + ) external { + require(_collaborators.length == _shares.length, "Mismatch"); + + listings[++listingCount] = Listing({ + seller: msg.sender, + nft: _nft, + tokenId: _tokenId, + price: _price, + collaborators: _collaborators, + shares: _shares + }); + + emit Listed(listingCount, msg.sender, _price); + } + + function buyNFT(uint256 _id) external payable { + Listing storage l = listings[_id]; + require(msg.value == l.price, "Wrong price"); + + uint256 totalShares = 0; + for (uint256 i = 0; i < l.shares.length; i++) { + totalShares += l.shares[i]; + } + + for (uint256 i = 0; i < l.collaborators.length; i++) { + uint256 payout = (msg.value * l.shares[i]) / totalShares; + payable(l.collaborators[i]).transfer(payout); + } + + IERC721Token(l.nft).transferFrom(l.seller, msg.sender, l.tokenId); + emit Purchased(_id, msg.sender); + } +} diff --git a/DynamicRoyalty/Readme.md b/DynamicRoyalty/Readme.md new file mode 100644 index 0000000..713ca75 --- /dev/null +++ b/DynamicRoyalty/Readme.md @@ -0,0 +1,168 @@ +# DynamicRoyalty + +## Contract Name + +**RoyaltyMarketplace** + +--- + +## Overview + +The `RoyaltyMarketplace` contract is a decentralized **NFT marketplace** that enables **collaborative sales** with **dynamic royalty distribution**. It allows NFT owners to list their ERC-721 tokens for sale while automatically distributing proceeds among multiple collaborators based on predefined shares. + +It securely handles: +- **NFT listings** with collaborative ownership +- **Dynamic royalty distribution** among collaborators +- **Automatic payment splitting** based on shares +- **Event tracking** for all marketplace activities + +This project is fully compatible with **QRemix IDE** and can be deployed seamlessly. + +--- + +## Features + +- List ERC-721 NFTs for collaborative sale +- Define multiple collaborators with custom share percentages +- Automatic royalty distribution upon purchase +- Secure handling of funds and NFT transfers +- Fully tested on **Quranium Testnet** +- Easily integrable with **QRemix** + +--- + +## Prerequisites + +- **MetaMask or QSafe** wallet +- **Testnet ETH or QRN** +- **QRemix IDE** ([https://qremix.org](https://qremix.org)) +- Basic understanding of **Solidity** & **ERC721** + +--- + +## Contract Functions + +### 1. listNFT + +```solidity +function listNFT(address _nft, uint256 _tokenId, uint256 _price, address[] memory _collaborators, uint256[] memory _shares) external +``` + +- **Purpose**: List ERC721 NFTs for collaborative sale with royalty distribution. +- **Arguments**: + - `_nft`: ERC721 contract address + - `_tokenId`: NFT token ID + - `_price`: Sale price in wei + - `_collaborators`: Array of collaborator addresses + - `_shares`: Array of share amounts for each collaborator +- **Emits**: `Listed(id, seller, price)` +- **Requires**: + - Collaborators and shares arrays must have equal length + - Owner must approve the contract for NFT transfers + +--- + +### 2. buyNFT + +```solidity +function buyNFT(uint256 _id) external payable +``` + +- **Purpose**: Purchase an NFT and automatically distribute royalties to collaborators. +- **Arguments**: + - `_id`: Listing ID to purchase +- **Emits**: `Purchased(id, buyer)` +- **Requires**: + - `msg.value` must equal the exact listing price + - NFT must be available for purchase + +--- + +## Events + +| Event | Description | +|------------------|----------------------------------------| +| Listed | Emitted when an NFT is listed for sale | +| Purchased | Emitted when an NFT is purchased | + +--- + +## Error Handling + +- Reverts if: + - Collaborators and shares arrays length mismatch + - Incorrect payment amount sent + - NFT transfer fails + - Invalid listing ID provided + +--- + +## Security Considerations + +- **Automatic royalty distribution** prevents manual intervention +- **Proportional share calculation** ensures fair distribution +- **Direct NFT transfer** from seller to buyer +- **Immutable listing data** once created + +--- + +## Deployment Guide (QRemix) + +### Step 1: Setup +- Open [https://qremix.org](https://qremix.org) +- Create a folder: `DynamicRoyalty/` +- Add `DynamicRoyalty.sol` and paste the contract + +### Step 2: Compile +- Select compiler `0.8.0+` +- Click **Compile** + +### Step 3: Deploy +- Go to **Deploy & Run Transactions** +- Select **Injected Provider** (MetaMask/QSafe) +- Connect to **Quranium Testnet** +- Deploy the contract + +--- + +## Testing Guide + +### 1. Listing an NFT with Collaborators +```solidity +listNFT(_nftAddress, _tokenId, _price, [_collaborator1, _collaborator2], [_share1, _share2]) +``` + +### 2. Purchasing an NFT +```solidity +buyNFT(_listingId) // send exact price in ETH +``` + +### 3. Checking Listing Details +```solidity +listings(_listingId) // returns seller, nft, tokenId, price, collaborators, shares +``` + +--- + +## Gas Optimization Tips + +- Use **memory** over **storage** for temporary arrays +- **Batch operations** where possible +- Minimize **loop iterations** in royalty distribution + +--- + +## Future Improvements + +- **ERC-1155 support** for batch NFTs +- **Auction functionality** with royalty distribution +- **Time-based listing expiration** +- **Advanced royalty calculation** algorithms + +--- + +## License + +MIT License © 2025 Quranium Labs + +-- diff --git a/Escrow/escrow.sol b/Escrow/escrow.sol new file mode 100644 index 0000000..6b29b4c --- /dev/null +++ b/Escrow/escrow.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Escrow { + enum Status { Pending, Completed, Disputed, Refunded } + + struct Deal { + address buyer; + address seller; + uint256 amount; + Status status; + } + + mapping(uint256 => Deal) public deals; + uint256 public dealCount; + address public arbitrator; + + event DealCreated(uint256 dealId, address buyer, address seller, uint256 amount); + event DealCompleted(uint256 dealId); + event DealRefunded(uint256 dealId); + + constructor(address _arbitrator) { + arbitrator = _arbitrator; + } + + function createDeal(address _seller) external payable { + deals[++dealCount] = Deal(msg.sender, _seller, msg.value, Status.Pending); + emit DealCreated(dealCount, msg.sender, _seller, msg.value); + } + + function completeDeal(uint256 _id) external { + Deal storage d = deals[_id]; + require(msg.sender == d.buyer, "Not buyer"); + require(d.status == Status.Pending, "Invalid status"); + + d.status = Status.Completed; + payable(d.seller).transfer(d.amount); + emit DealCompleted(_id); + } + + function raiseDispute(uint256 _id) external { + Deal storage d = deals[_id]; + require(msg.sender == d.buyer || msg.sender == d.seller, "Not involved"); + require(d.status == Status.Pending, "Invalid status"); + d.status = Status.Disputed; + } + + function resolveDispute(uint256 _id, bool refundBuyer) external { + require(msg.sender == arbitrator, "Not arbitrator"); + Deal storage d = deals[_id]; + require(d.status == Status.Disputed, "Invalid status"); + + d.status = refundBuyer ? Status.Refunded : Status.Completed; + address recipient = refundBuyer ? d.buyer : d.seller; + payable(recipient).transfer(d.amount); + emit DealRefunded(_id); + } +} diff --git a/Escrow/readme.md b/Escrow/readme.md new file mode 100644 index 0000000..87b0e77 --- /dev/null +++ b/Escrow/readme.md @@ -0,0 +1,218 @@ +# Escrow + +## Contract Name + +**Escrow** + +--- + +## Overview + +The `Escrow` contract is a decentralized **escrow service** that facilitates secure transactions between buyers and sellers with **arbitration support**. It holds funds in escrow until both parties agree on completion or an arbitrator resolves disputes. + +It securely handles: +- **Fund escrow** for secure transactions +- **Dispute resolution** through arbitration +- **Automatic refunds** when disputes are resolved +- **Event tracking** for all escrow activities + +This project is fully compatible with **QRemix IDE** and can be deployed seamlessly. + +--- + +## Features + +- Create secure escrow deals between buyers and sellers +- Dispute resolution mechanism with arbitrator +- Automatic fund distribution upon completion +- Refund capability for disputed transactions +- Fully tested on **Quranium Testnet** +- Easily integrable with **QRemix** + +--- + +## Prerequisites + +- **MetaMask or QSafe** wallet +- **Testnet ETH or QRN** +- **QRemix IDE** ([https://qremix.org](https://qremix.org)) +- Basic understanding of **Solidity** & **Escrow mechanisms** + +--- + +## Contract Functions + +### 1. createDeal + +```solidity +function createDeal(address _seller) external payable +``` + +- **Purpose**: Create a new escrow deal with a seller. +- **Arguments**: + - `_seller`: Address of the seller +- **Emits**: `DealCreated(dealId, buyer, seller, amount)` +- **Requires**: + - `msg.value` must be greater than 0 + - Valid seller address + +--- + +### 2. completeDeal + +```solidity +function completeDeal(uint256 _id) external +``` + +- **Purpose**: Complete an escrow deal and release funds to seller. +- **Arguments**: + - `_id`: Deal ID to complete +- **Emits**: `DealCompleted(id)` +- **Requires**: + - Only the buyer can complete the deal + - Deal must be in Pending status + +--- + +### 3. raiseDispute + +```solidity +function raiseDispute(uint256 _id) external +``` + +- **Purpose**: Raise a dispute for an escrow deal. +- **Arguments**: + - `_id`: Deal ID to dispute +- **Requires**: + - Only buyer or seller can raise dispute + - Deal must be in Pending status + +--- + +### 4. resolveDispute + +```solidity +function resolveDispute(uint256 _id, bool refundBuyer) external +``` + +- **Purpose**: Resolve a disputed deal by arbitrator. +- **Arguments**: + - `_id`: Deal ID to resolve + - `refundBuyer`: Whether to refund buyer (true) or pay seller (false) +- **Emits**: `DealRefunded(id)` +- **Requires**: + - Only arbitrator can resolve disputes + - Deal must be in Disputed status + +--- + +## Deal Status + +| Status | Description | +|-----------|----------------------------------------| +| Pending | Deal created, awaiting completion | +| Completed | Deal completed, funds released to seller | +| Disputed | Dispute raised, awaiting arbitration | +| Refunded | Dispute resolved, funds refunded to buyer | + +--- + +## Events + +| Event | Description | +|------------------|----------------------------------------| +| DealCreated | Emitted when a new escrow deal is created | +| DealCompleted | Emitted when a deal is completed | +| DealRefunded | Emitted when a deal is refunded | + +--- + +## Error Handling + +- Reverts if: + - Unauthorized user attempts to complete deal + - Deal is not in correct status for operation + - Invalid deal ID provided + - Non-arbitrator attempts to resolve disputes + +--- + +## Security Considerations + +- **Arbitrator role** for dispute resolution +- **Status-based access control** prevents unauthorized actions +- **Direct fund transfers** to prevent reentrancy +- **Immutable deal data** once created + +--- + +## Deployment Guide (QRemix) + +### Step 1: Setup +- Open [https://qremix.org](https://qremix.org) +- Create a folder: `Escrow/` +- Add `escrow.sol` and paste the contract + +### Step 2: Compile +- Select compiler `0.8.0+` +- Click **Compile** + +### Step 3: Deploy +- Go to **Deploy & Run Transactions** +- Select **Injected Provider** (MetaMask/QSafe) +- Connect to **Quranium Testnet** +- Deploy with arbitrator address as constructor parameter + +--- + +## Testing Guide + +### 1. Creating an Escrow Deal +```solidity +createDeal(_sellerAddress) // send required ETH +``` + +### 2. Completing a Deal +```solidity +completeDeal(_dealId) // only buyer can call +``` + +### 3. Raising a Dispute +```solidity +raiseDispute(_dealId) // buyer or seller can call +``` + +### 4. Resolving a Dispute +```solidity +resolveDispute(_dealId, _refundBuyer) // only arbitrator +``` + +### 5. Checking Deal Status +```solidity +deals(_dealId) // returns buyer, seller, amount, status +``` + +--- + +## Gas Optimization Tips + +- Use **enum** for status management +- **Minimize storage operations** in loops +- **Batch dispute resolutions** when possible + +--- + +## Future Improvements + +- **Time-based automatic completion** +- **Multi-signature arbitration** +- **Escrow fee system** +- **Integration with payment tokens** + +--- + +## License + +MIT License © 2025 Quranium Labs + +-- diff --git a/LendingNFT.sol/Lending.sol b/LendingNFT.sol/Lending.sol new file mode 100644 index 0000000..260a368 --- /dev/null +++ b/LendingNFT.sol/Lending.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IERC721 { + function safeTransferFrom(address from, address to, uint256 tokenId) external; +} + +contract NFTRental { + struct Rental { + address owner; + address renter; + address nft; + uint256 tokenId; + uint256 price; + uint256 expiresAt; + bool rented; + } + + uint256 public rentalCount; + mapping(uint256 => Rental) public rentals; + + event NFTListed(uint256 indexed rentalId, address owner, address nft, uint256 tokenId, uint256 price); + event NFTRented(uint256 indexed rentalId, address renter, uint256 expiresAt); + event NFTReturned(uint256 indexed rentalId); + + function listNFT(address _nft, uint256 _tokenId, uint256 _price) external { + IERC721(_nft).safeTransferFrom(msg.sender, address(this), _tokenId); + + rentals[++rentalCount] = Rental({ + owner: msg.sender, + renter: address(0), + nft: _nft, + tokenId: _tokenId, + price: _price, + expiresAt: 0, + rented: false + }); + + emit NFTListed(rentalCount, msg.sender, _nft, _tokenId, _price); + } + + function rentNFT(uint256 _rentalId, uint256 _duration) external payable { + Rental storage r = rentals[_rentalId]; + require(!r.rented, "Already rented"); + require(msg.value == r.price, "Incorrect price"); + + r.renter = msg.sender; + r.expiresAt = block.timestamp + _duration; + r.rented = true; + + emit NFTRented(_rentalId, msg.sender, r.expiresAt); + } + + function returnNFT(uint256 _rentalId) external { + Rental storage r = rentals[_rentalId]; + require(r.rented, "Not rented"); + require(block.timestamp >= r.expiresAt, "Rental period not over"); + + IERC721(r.nft).safeTransferFrom(address(this), r.owner, r.tokenId); + payable(r.owner).transfer(r.price); + + r.rented = false; + emit NFTReturned(_rentalId); + } +} diff --git a/LendingNFT.sol/Readme.md b/LendingNFT.sol/Readme.md new file mode 100644 index 0000000..17aaf7b --- /dev/null +++ b/LendingNFT.sol/Readme.md @@ -0,0 +1,200 @@ + +# NFTRental + +## Contract Name + +**NFTRental** + +--- + +## Overview + +The `NFTRental` contract is a decentralized **NFT rental marketplace** that allows **NFT owners** to list their ERC-721 tokens for rent and enables **renters** to borrow them for a specific duration **without transferring ownership**. + +It securely handles: +- **NFT custody** +- **Rental payments** +- **Automatic returns** after expiration +- **Event tracking** for all actions + +This project is fully compatible with **QRemix IDE** and can be deployed seamlessly. + +--- + +## Features + +- List ERC-721 NFTs for rent +- Rent NFTs for a defined period +- Automatic NFT return after rental expiry +- Secure handling of funds +- Fully tested on **Quranium Testnet** +- Easily integrable with **QRemix** + +--- + +## Prerequisites + +- **MetaMask or QSafe** wallet +- **Testnet ETH or QRN** +- **QRemix IDE** ([https://qremix.org](https://qremix.org)) +- Basic understanding of **Solidity** & **ERC721** + +--- + +## Contract Functions + +### 1. listNFT + +```solidity +function listNFT(address _nft, uint256 _tokenId, uint256 _price) external +``` + +- **Purpose**: List ERC721 NFTs for rent. +- **Arguments**: + - `_nft`: ERC721 contract address + - `_tokenId`: NFT token ID + - `_price`: Rental price in wei +- **Emits**: `NFTListed(rentalId, owner, nft, tokenId, price)` +- **Requires**: + - Owner must approve the contract for NFT transfers. + +--- + +### 2. rentNFT + +```solidity +function rentNFT(uint256 _rentalId, uint256 _duration) external payable +``` + +- **Purpose**: Rent an NFT for a fixed duration. +- **Arguments**: + - `_rentalId`: ID of the rental listing + - `_duration`: Duration in seconds +- **Emits**: `NFTRented(rentalId, renter, expiresAt)` +- **Requires**: + - NFT must not be rented already. + - `msg.value` must equal the rental price. + +--- + +### 3. returnNFT + +```solidity +function returnNFT(uint256 _rentalId) external +``` + +- **Purpose**: Return rented NFTs to owners. +- **Arguments**: + - `_rentalId`: Rental listing ID +- **Emits**: `NFTReturned(rentalId)` +- **Requires**: + - NFT must be rented. + - Rental period must have expired. + +--- + +### 4. withdrawEarnings + +```solidity +function withdrawEarnings() external +``` + +- **Purpose**: NFT owners withdraw rental earnings. +- **Access**: Only owners. + +--- + +## Events + +| Event | Description | +|------------------|----------------------------------------| +| NFTListed | Emitted when an NFT is listed | +| NFTRented | Emitted when an NFT is rented | +| NFTReturned | Emitted when an NFT is returned | + +--- + +## Error Handling + +- Reverts if: + - NFT is already rented + - Insufficient payment sent + - Rental period has not expired + - Unauthorized actions attempted + +--- + +## Security Considerations + +- Uses **ReentrancyGuard** for fund transfers. +- Ensures **owner-only listing rights**. +- Prevents renters from **transferring NFTs**. +- Protects against expired rentals. + +--- + +## Deployment Guide (QRemix) + +### Step 1: Setup +- Open [https://qremix.org](https://qremix.org) +- Create a folder: `NFTRental/` +- Add `NFTRental.sol` and paste the contract. + +### Step 2: Compile +- Select compiler `0.8.20+` +- Click **Compile**. + +### Step 3: Deploy +- Go to **Deploy & Run Transactions** +- Select **Injected Provider** (MetaMask/QSafe) +- Connect to **Quranium Testnet** +- Deploy the contract. + +--- + +## Testing Guide + +### 1. Listing an NFT +```solidity +listNFT(_nftAddress, _tokenId, _price) +``` + +### 2. Renting an NFT +```solidity +rentNFT(_rentalId, _duration) // send required ETH +``` + +### 3. Returning an NFT +```solidity +returnNFT(_rentalId) +``` + +### 4. Withdrawing Earnings +```solidity +withdrawEarnings() +``` + +--- + +## Gas Optimization Tips + +- Use **memory** over **storage** where possible. +- Use **unchecked** math for safe operations. +- Minimize event emissions. + +--- + +## Future Improvements + +- ERC-1155 support +- Dynamic pricing system +- NFT rental auctions +- Integrated off-chain metadata + +--- + +## License + +MIT License © 2025 Quranium Labs + +-- \ No newline at end of file diff --git a/ReputationSystem/Readme.md b/ReputationSystem/Readme.md new file mode 100644 index 0000000..22a43a8 --- /dev/null +++ b/ReputationSystem/Readme.md @@ -0,0 +1,190 @@ +# ReputationSystem + +## Contract Name + +**ReputationSystem** + +--- + +## Overview + +The `ReputationSystem` contract is a decentralized **reputation management system** that allows **moderators** to manage user reputation scores within a community. It provides a transparent and tamper-proof way to track user behavior and contributions. + +It securely handles: +- **Reputation score management** for users +- **Moderator role assignment** and management +- **Reputation adjustments** (positive and negative) +- **Event tracking** for all reputation changes + +This project is fully compatible with **QRemix IDE** and can be deployed seamlessly. + +--- + +## Features + +- Assign and manage moderator roles +- Increase user reputation scores +- Decrease user reputation scores with safety checks +- Transparent reputation tracking with events +- Fully tested on **Quranium Testnet** +- Easily integrable with **QRemix** + +--- + +## Prerequisites + +- **MetaMask or QSafe** wallet +- **Testnet ETH or QRN** +- **QRemix IDE** ([https://qremix.org](https://qremix.org)) +- Basic understanding of **Solidity** & **Access Control** + +--- + +## Contract Functions + +### 1. setModerator + +```solidity +function setModerator(address _mod, bool _status) external +``` + +- **Purpose**: Grant or revoke moderator privileges for an address. +- **Arguments**: + - `_mod`: Address to set moderator status for + - `_status`: True to grant moderator role, false to revoke +- **Requires**: + - Only existing moderators can call this function + +--- + +### 2. giveReputation + +```solidity +function giveReputation(address _user, uint256 _score) external +``` + +- **Purpose**: Increase a user's reputation score. +- **Arguments**: + - `_user`: Address of the user to reward + - `_score`: Amount of reputation to add +- **Emits**: `ReputationUpdated(user, newScore)` +- **Requires**: + - Only moderators can call this function + +--- + +### 3. reduceReputation + +```solidity +function reduceReputation(address _user, uint256 _score) external +``` + +- **Purpose**: Decrease a user's reputation score. +- **Arguments**: + - `_user`: Address of the user to penalize + - `_score`: Amount of reputation to subtract +- **Emits**: `ReputationUpdated(user, newScore)` +- **Requires**: + - Only moderators can call this function + - User must have sufficient reputation to reduce + +--- + +## Events + +| Event | Description | +|------------------|----------------------------------------| +| ReputationUpdated| Emitted when a user's reputation changes | + +--- + +## Error Handling + +- Reverts if: + - Non-moderator attempts to modify reputation + - Non-moderator attempts to set moderator status + - Attempting to reduce reputation below zero + - Invalid addresses provided + +--- + +## Security Considerations + +- **Moderator-only access** for reputation changes +- **Underflow protection** for reputation reduction +- **Immutable reputation history** through events +- **Transparent moderator management** + +--- + +## Deployment Guide (QRemix) + +### Step 1: Setup +- Open [https://qremix.org](https://qremix.org) +- Create a folder: `ReputationSystem/` +- Add `Reputation.sol` and paste the contract + +### Step 2: Compile +- Select compiler `0.8.0+` +- Click **Compile** + +### Step 3: Deploy +- Go to **Deploy & Run Transactions** +- Select **Injected Provider** (MetaMask/QSafe) +- Connect to **Quranium Testnet** +- Deploy the contract (deployer becomes first moderator) + +--- + +## Testing Guide + +### 1. Setting Moderator Status +```solidity +setModerator(_moderatorAddress, true) // grant moderator role +setModerator(_moderatorAddress, false) // revoke moderator role +``` + +### 2. Giving Reputation +```solidity +giveReputation(_userAddress, _score) // only moderators +``` + +### 3. Reducing Reputation +```solidity +reduceReputation(_userAddress, _score) // only moderators +``` + +### 4. Checking Reputation +```solidity +reputation(_userAddress) // returns current reputation score +``` + +### 5. Checking Moderator Status +```solidity +moderators(_address) // returns true if address is moderator +``` + +--- + +## Gas Optimization Tips + +- Use **mapping** for efficient reputation lookups +- **Minimize storage operations** in loops +- **Batch reputation updates** when possible + +--- + +## Future Improvements + +- **Time-decay reputation** system +- **Community voting** for reputation changes +- **Reputation tiers** with different privileges +- **Integration with other DeFi protocols** + +--- + +## License + +MIT License © 2025 Quranium Labs + +-- diff --git a/ReputationSystem/Reputation.sol b/ReputationSystem/Reputation.sol new file mode 100644 index 0000000..66699e6 --- /dev/null +++ b/ReputationSystem/Reputation.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract ReputationSystem { + mapping(address => uint256) public reputation; + mapping(address => bool) public moderators; + + event ReputationUpdated(address user, uint256 score); + + constructor() { + moderators[msg.sender] = true; + } + + function setModerator(address _mod, bool _status) external { + require(moderators[msg.sender], "Not authorized"); + moderators[_mod] = _status; + } + + function giveReputation(address _user, uint256 _score) external { + require(moderators[msg.sender], "Only mods"); + reputation[_user] += _score; + emit ReputationUpdated(_user, reputation[_user]); + } + + function reduceReputation(address _user, uint256 _score) external { + require(moderators[msg.sender], "Only mods"); + require(reputation[_user] >= _score, "Not enough rep"); + reputation[_user] -= _score; + emit ReputationUpdated(_user, reputation[_user]); + } +} diff --git a/SubscriptionAccess.sol/Access.sol b/SubscriptionAccess.sol/Access.sol new file mode 100644 index 0000000..8b8cc45 --- /dev/null +++ b/SubscriptionAccess.sol/Access.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract SubscriptionAccess { + struct Plan { + uint256 price; + uint256 duration; + } + + struct Subscriber { + uint256 planId; + uint256 expiry; + } + + mapping(uint256 => Plan) public plans; + mapping(address => Subscriber) public subscribers; + uint256 public planCount; + address public owner; + + event Subscribed(address user, uint256 planId, uint256 expiry); + + constructor() { + owner = msg.sender; + } + + function createPlan(uint256 _price, uint256 _duration) external { + require(msg.sender == owner, "Only owner"); + plans[++planCount] = Plan(_price, _duration); + } + + function subscribe(uint256 _planId) external payable { + Plan memory plan = plans[_planId]; + require(plan.price > 0, "Invalid plan"); + require(msg.value == plan.price, "Incorrect price"); + + subscribers[msg.sender] = Subscriber(_planId, block.timestamp + plan.duration); + emit Subscribed(msg.sender, _planId, block.timestamp + plan.duration); + } + + function hasAccess(address _user) external view returns (bool) { + return subscribers[_user].expiry > block.timestamp; + } + + function withdraw() external { + require(msg.sender == owner, "Not owner"); + payable(owner).transfer(address(this).balance); + } +} diff --git a/SubscriptionAccess.sol/readme.md b/SubscriptionAccess.sol/readme.md new file mode 100644 index 0000000..fcc1089 --- /dev/null +++ b/SubscriptionAccess.sol/readme.md @@ -0,0 +1,223 @@ +# SubscriptionAccess + +## Contract Name + +**SubscriptionAccess** + +--- + +## Overview + +The `SubscriptionAccess` contract is a decentralized **subscription service** that manages **access control** through **time-based subscriptions**. It allows users to subscribe to different plans and provides access verification for protected content or services. + +It securely handles: +- **Subscription plan management** with pricing and duration +- **User subscription tracking** with expiration dates +- **Access verification** for protected resources +- **Event tracking** for all subscription activities + +This project is fully compatible with **QRemix IDE** and can be deployed seamlessly. + +--- + +## Features + +- Create and manage subscription plans with custom pricing +- User subscription with automatic expiration tracking +- Real-time access verification for protected content +- Owner-controlled plan creation and fund withdrawal +- Fully tested on **Quranium Testnet** +- Easily integrable with **QRemix** + +--- + +## Prerequisites + +- **MetaMask or QSafe** wallet +- **Testnet ETH or QRN** +- **QRemix IDE** ([https://qremix.org](https://qremix.org)) +- Basic understanding of **Solidity** & **Subscription Models** + +--- + +## Contract Functions + +### 1. createPlan + +```solidity +function createPlan(uint256 _price, uint256 _duration) external +``` + +- **Purpose**: Create a new subscription plan with pricing and duration. +- **Arguments**: + - `_price`: Subscription price in wei + - `_duration`: Subscription duration in seconds +- **Requires**: + - Only contract owner can create plans + +--- + +### 2. subscribe + +```solidity +function subscribe(uint256 _planId) external payable +``` + +- **Purpose**: Subscribe to a plan and gain access for the specified duration. +- **Arguments**: + - `_planId`: ID of the plan to subscribe to +- **Emits**: `Subscribed(user, planId, expiry)` +- **Requires**: + - Plan must exist and be valid + - `msg.value` must equal the plan price + +--- + +### 3. hasAccess + +```solidity +function hasAccess(address _user) external view returns (bool) +``` + +- **Purpose**: Check if a user has active subscription access. +- **Arguments**: + - `_user`: Address to check access for +- **Returns**: `true` if user has active subscription, `false` otherwise + +--- + +### 4. withdraw + +```solidity +function withdraw() external +``` + +- **Purpose**: Withdraw all contract funds to the owner. +- **Requires**: + - Only contract owner can withdraw funds + +--- + +## Data Structures + +### Plan +```solidity +struct Plan { + uint256 price; // Subscription price in wei + uint256 duration; // Duration in seconds +} +``` + +### Subscriber +```solidity +struct Subscriber { + uint256 planId; // ID of subscribed plan + uint256 expiry; // Subscription expiration timestamp +} +``` + +--- + +## Events + +| Event | Description | +|------------------|----------------------------------------| +| Subscribed | Emitted when a user subscribes to a plan | + +--- + +## Error Handling + +- Reverts if: + - Non-owner attempts to create plans + - Non-owner attempts to withdraw funds + - Invalid plan ID provided + - Incorrect payment amount sent + - Attempting to subscribe to non-existent plan + +--- + +## Security Considerations + +- **Owner-only access** for plan creation and fund withdrawal +- **Time-based access control** with automatic expiration +- **Direct fund transfers** to prevent reentrancy +- **Immutable subscription data** once created + +--- + +## Deployment Guide (QRemix) + +### Step 1: Setup +- Open [https://qremix.org](https://qremix.org) +- Create a folder: `SubscriptionAccess/` +- Add `Access.sol` and paste the contract + +### Step 2: Compile +- Select compiler `0.8.0+` +- Click **Compile** + +### Step 3: Deploy +- Go to **Deploy & Run Transactions** +- Select **Injected Provider** (MetaMask/QSafe) +- Connect to **Quranium Testnet** +- Deploy the contract (deployer becomes owner) + +--- + +## Testing Guide + +### 1. Creating Subscription Plans +```solidity +createPlan(_priceInWei, _durationInSeconds) // only owner +``` + +### 2. Subscribing to a Plan +```solidity +subscribe(_planId) // send exact price in ETH +``` + +### 3. Checking Access Status +```solidity +hasAccess(_userAddress) // returns true/false +``` + +### 4. Checking Plan Details +```solidity +plans(_planId) // returns price and duration +``` + +### 5. Checking User Subscription +```solidity +subscribers(_userAddress) // returns planId and expiry +``` + +### 6. Withdrawing Funds +```solidity +withdraw() // only owner +``` + +--- + +## Gas Optimization Tips + +- Use **view functions** for access checks +- **Minimize storage operations** in loops +- **Batch plan creation** when possible + +--- + +## Future Improvements + +- **Recurring subscription** payments +- **Tiered access levels** with different privileges +- **Subscription upgrades** and downgrades +- **Integration with payment tokens** + +--- + +## License + +MIT License © 2025 Quranium Labs + +-- From 398250a80c0efd07c315935d47cdf2bd4ad8d02b Mon Sep 17 00:00:00 2001 From: Ayan Date: Tue, 2 Sep 2025 01:03:41 +0530 Subject: [PATCH 09/10] Add Tally contract with simple counter (increase/decrease), PiggyBank contract with deposit and owner-only withdraw --- PiggyBank/PiggyBank.sol | 12 +++++ PiggyBank/README.md | 106 ++++++++++++++++++++++++++++++++++++++++ Tally/README.md | 89 +++++++++++++++++++++++++++++++++ Tally/Tally.sol | 14 ++++++ 4 files changed, 221 insertions(+) create mode 100644 PiggyBank/PiggyBank.sol create mode 100644 PiggyBank/README.md create mode 100644 Tally/README.md create mode 100644 Tally/Tally.sol diff --git a/PiggyBank/PiggyBank.sol b/PiggyBank/PiggyBank.sol new file mode 100644 index 0000000..c9b81b2 --- /dev/null +++ b/PiggyBank/PiggyBank.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +contract PiggyBank { + address public owner; + constructor() { owner = msg.sender; } + receive() external payable {} + function withdraw() external { + require(msg.sender == owner, "not owner"); + payable(owner).transfer(address(this).balance); + } +} diff --git a/PiggyBank/README.md b/PiggyBank/README.md new file mode 100644 index 0000000..32da55b --- /dev/null +++ b/PiggyBank/README.md @@ -0,0 +1,106 @@ +# PiggyBank + +## Contract Name +PiggyBank + +## Overview +The **PiggyBank** smart contract is a simple Ethereum wallet that allows the contract owner to deposit and withdraw funds. +It demonstrates the use of the `receive` function, ownership restrictions, and secure withdrawal logic. + +It can be deployed and tested in the **QRemix IDE** using the JavaScript VM (local testing) or on testnets like the **Quranium Testnet** or **Sepolia**. + +--- + +## Prerequisites +To deploy and test the contract, you need: + +* **MetaMask or QSafe** (optional): For testnet deployments. +* **Test ETH or QRN**: Required for deploying to Sepolia or Quranium testnet. +* **QRemix IDE**: Access at [qremix.org](https://qremix.org). +* **Basic Solidity Knowledge**: Understanding of contract deployment and payable functions. + +--- + +## Contract Details + +### State Variables +- **`owner`** (`address`): + Stores the address of the contract owner (the account that deployed the contract). + +### Functions + +#### constructor() +- **Purpose**: Sets the `owner` of the contract to the deployer’s address. +- **Access**: Called only once at deployment. + +#### receive() external payable +- **Purpose**: Allows the contract to receive ETH/QRN directly. +- **Usage**: Send funds to the contract address using MetaMask, Remix, or any wallet. + +#### withdraw() +- **Purpose**: Withdraws the entire balance of the contract to the owner’s address. +- **Access**: Only the `owner` can call. +- **Restrictions**: + - Reverts with `"not owner"` if called by anyone other than the owner. + - Transfers the full contract balance to the owner. + +--- + +## Deployment and Testing in QRemix IDE + +### Step 1: Setup +1. Open [qremix.org](https://qremix.org). +2. Create a new file: `PiggyBank.sol`. +3. Copy and paste the contract code. + +### Step 2: Compilation +1. Go to the **Solidity Compiler** tab. +2. Select compiler version **0.8.19** or higher. +3. Compile `PiggyBank.sol`. + +### Step 3: Deployment + +#### For Quranium Testnet: +1. Go to the **Deploy & Run Transactions** tab. +2. Select **Injected Provider - MetaMask** as environment. +3. Connect MetaMask/QSafe to **Quranium Testnet**. +4. Deploy the contract. + - The deployer will automatically become the **owner**. + +#### For JavaScript VM (Local Testing): +1. Go to the **Deploy & Run Transactions** tab. +2. Select **JavaScript VM** as environment. +3. Deploy the contract locally. + +--- + +## Step 4: Testing + +1. **Deposit funds**: + - Select the deployed contract. + - In the Remix "Value" field, enter some ETH/QRN (e.g., `1 ether`). + - Click **Transact** on the contract → funds are stored in the PiggyBank. + +2. **Check balance**: + - Use the "At Address" section or call `address(this).balance` in Remix to see funds. + +3. **Withdraw funds**: + - Call `withdraw()` from the **owner** account. + - The full balance transfers to the owner’s wallet. + +4. **Test restrictions**: + - Switch to a **non-owner account**. + - Try calling `withdraw()` → transaction will fail with `"not owner"`. + +--- + +## License +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the contract file. + +--- + +## Support +For help or more information: +* **QRemix Docs**: [docs.qremix.org](https://docs.qremix.org) + diff --git a/Tally/README.md b/Tally/README.md new file mode 100644 index 0000000..4f18dbf --- /dev/null +++ b/Tally/README.md @@ -0,0 +1,89 @@ +# Tally Counter + +## Contract Name +Tally + +## Overview +The **Tally** smart contract is a simple counter that allows users to increase or decrease a stored integer value. +It can be deployed and tested in the **QRemix IDE** using the JavaScript VM (local testing) or on testnets such as the **Quranium Testnet** or **Sepolia**. + +This contract is useful for learning Solidity basics and testing increment/decrement logic in a blockchain environment. + +--- + +## Prerequisites +To deploy and test the contract, you need: + +* **MetaMask or QSafe** (optional): For testnet deployments. +* **Test ETH or QRN**: Required for deploying to Sepolia or Quranium testnet. +* **QRemix IDE**: Access at [qremix.org](https://qremix.org). +* **Basic Solidity Knowledge**: Understanding of contract deployment and function calls. + +--- + +## Contract Details + +### State Variables +- **`value`** (`int256`): + The stored integer that can be increased or decreased. + +### Functions + +#### increase() +- **Purpose**: Increment the `value` by 1. +- **Parameters**: None +- **Access**: Public (anyone can call) + +#### decrease() +- **Purpose**: Decrement the `value` by 1. +- **Parameters**: None +- **Access**: Public (anyone can call) + +--- + +## Deployment and Testing in QRemix IDE + +### Step 1: Setup +1. Open [qremix.org](https://qremix.org). +2. Create a new file: `Tally.sol`. +3. Copy and paste the contract code. + +### Step 2: Compilation +1. Go to the **Solidity Compiler** tab. +2. Select compiler version **0.8.19** or higher. +3. Compile `Tally.sol`. + +### Step 3: Deployment + +#### For Quranium Testnet: +1. Go to the **Deploy & Run Transactions** tab. +2. Select **Injected Provider - MetaMask** as environment. +3. Connect MetaMask/QSafe to **Quranium Testnet**. +4. Deploy the contract. + +#### For JavaScript VM (Local Testing): +1. Go to the **Deploy & Run Transactions** tab. +2. Select **JavaScript VM** as environment. +3. Deploy the contract locally. + +--- + +## Step 4: Testing + +1. **Check initial value**: Call `value()` → should return `0` (default). +2. **Increase counter**: Call `increase()` → `value` increases by 1 each call. +3. **Decrease counter**: Call `decrease()` → `value` decreases by 1 each call. +4. **Multiple accounts**: Switch accounts in QRemix/MetaMask and test that anyone can call the functions. +5. **Negative values**: Since `value` is `int256`, it can go below zero. + +--- + +## License +This project is licensed under the MIT License. +See `SPDX-License-Identifier: MIT` in the contract file. + +--- + +## Support +For help or more information: +* **QRemix Docs**: [docs.qremix.org](https://docs.qremix.org) diff --git a/Tally/Tally.sol b/Tally/Tally.sol new file mode 100644 index 0000000..ec69c53 --- /dev/null +++ b/Tally/Tally.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +contract Tally { + int256 public value; + + function increase() external { + value += 1; + } + + function decrease() external { + value -= 1; + } +} From 2f681d5151fbaa67cf43556c678509b93c597da1 Mon Sep 17 00:00:00 2001 From: Nidhicodes Date: Sun, 14 Sep 2025 21:10:52 +0530 Subject: [PATCH 10/10] contracts added --- 3-Player-Lottery/Readme.md | 88 +++++++++ 3-Player-Lottery/lottery.sol | 98 ++++++++++ CharityDonation/Readme.md | 116 +++++++++++ CharityDonation/donation.sol | 49 +++++ Decentralized-TalentShow/Readme.md | 172 ++++++++++++++++ Decentralized-TalentShow/talentshow.sol | 211 ++++++++++++++++++++ Decentralized-ToDo/Readme.md | 104 ++++++++++ Decentralized-ToDo/de-to-do.sol | 40 ++++ SkillEndorsement/Readme.md | 167 ++++++++++++++++ SkillEndorsement/skill-endorse.sol | 248 ++++++++++++++++++++++++ 10 files changed, 1293 insertions(+) create mode 100644 3-Player-Lottery/Readme.md create mode 100644 3-Player-Lottery/lottery.sol create mode 100644 CharityDonation/Readme.md create mode 100644 CharityDonation/donation.sol create mode 100644 Decentralized-TalentShow/Readme.md create mode 100644 Decentralized-TalentShow/talentshow.sol create mode 100644 Decentralized-ToDo/Readme.md create mode 100644 Decentralized-ToDo/de-to-do.sol create mode 100644 SkillEndorsement/Readme.md create mode 100644 SkillEndorsement/skill-endorse.sol diff --git a/3-Player-Lottery/Readme.md b/3-Player-Lottery/Readme.md new file mode 100644 index 0000000..2febada --- /dev/null +++ b/3-Player-Lottery/Readme.md @@ -0,0 +1,88 @@ +# SimpleLottery Contract + +## Contract Name +SimpleLottery + +## Overview +SimpleLottery is a minimal, chain-agnostic smart contract that implements a trustless lottery game among exactly three participants. Each participant deposits a fixed entry fee (0.01 ether equivalent) into the contract. When all three participants have entered, the contract uses on-chain block data to select a winner pseudo-randomly and automatically transfers the full prize pool (0.03 ether equivalent) to the winner. + +The contract includes an owner-set timeout mechanism to refund participants if the lottery does not fill within a certain block period, ensuring funds are never locked indefinitely. + +## Prerequisites +To deploy and test the SimpleLottery contract, you will need: +* **Ethereum-compatible wallet**: MetaMask, Q-Safe (for Quranium), or any wallet configured for the target chain. +* **Test tokens (native coin of the chain)**: e.g., Sepolia ETH on Sepolia testnet, QRN on Quranium testnet, etc. These are required for gas fees and deposits. +* **Q-Remix**: To compile, deploy, and interact. +* **Basic Solidity knowledge**: Familiarity with contract deployment, interaction, and functions. + +## Contract Details + +### State Variables +- **players**: `address payable` — stores the three unique participants.[3] +- **playerCount**: `uint` — counts the currently joined players (max 3). +- **ENTRY_FEE**: `uint constant` — fixed entry fee, set to 0.01 ether (chain native). +- **winnerSelected**: `bool` — true when a winner has been picked. +- **winner**: `address` — winner’s address. +- **owner**: `address` — deployer of the contract, who can issue refunds on timeout. +- **timeoutBlock**: `uint` — block number after which refund is allowed if lottery incomplete. + +### Main Functions + +#### Constructor +- **Purpose**: Sets the contract owner and configures the timeout block for refunds. +- **Parameters**: + - `_timeoutBlock` (uint): block number after which refunds can be issued if lottery not complete. + +#### enter +- **Purpose**: Allows a unique participant to join by sending exactly 0.01 ether. +- **Requirements**: + - Game not finished. + - Less than 3 players currently. + - Caller not already entered. + - Sent value matches `ENTRY_FEE`. +- **Behavior**: + - Adds caller to players. + - Emits `PlayerJoined` event. + - Automatically picks winner and pays out when 3 players join. + +#### _pickWinner (internal) +- **Purpose**: Randomly selects one of the three players as winner using blockhash entropy. +- **Behavior**: + - Sets `winner`. + - Emits `WinnerSelected` event. + - Calls payout function. + +#### _payout (internal) +- **Purpose**: Transfers the entire contract balance to the winner. + +#### refund +- **Access**: Only owner. +- **Purpose**: Refunds all participants if the lottery didn't complete and timeout reached. +- **Requirements**: + - Lottery not finished. + - Current block number >= `timeoutBlock`. +- **Behavior:** + - Sends deposits back to players. + - Resets player count. + - Emits `RefundIssued` event. + +### Events +- **PlayerJoined(address indexed player)** — emitted when a player joins the lottery. +- **WinnerSelected(address indexed winner)** — emitted when a winner is picked. +- **RefundIssued()** — emitted when a refund is processed. + +*** + +## Usage Flow +1. Deploy contract on target EVM blockchain with a suitable timeout block. +2. Participants call `enter()` sending exactly the entry fee native coin. +3. After three participants join, winner is chosen and prize sent automatically. +4. If lottery fails to fill before timeout, owner can call `refund()` to return deposits. + +*** + +## Notes +- Randomness uses blockhash and is not secure against manipulations; for production, integrate Chainlink VRF or secure randomness oracles. +- ENTRY_FEE and timeoutBlock can be modified or parameterized to suit different chains or use-cases. + + diff --git a/3-Player-Lottery/lottery.sol b/3-Player-Lottery/lottery.sol new file mode 100644 index 0000000..6f41763 --- /dev/null +++ b/3-Player-Lottery/lottery.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +/// @title SimpleLottery - A minimal lottery contract for 3 players with timeout and refund +/// @notice Players join by paying 0.01 ether. When 3 players join, a random winner is chosen. +/// @dev Randomness uses blockhash, which is insecure for production. Owner can trigger refunds if timeout passes. +contract SimpleLottery { + address payable[3] public players; // Fixed array to store player addresses + uint public playerCount = 0; // Number of players currently entered + uint public constant ENTRY_FEE = 0.01 ether; // Entry fee for the lottery + bool public winnerSelected = false; // Flag to indicate if a winner was chosen + address public winner; // Address of the winner + address public owner; // Contract owner (deployer) + uint public timeoutBlock; // Block number after which refund is allowed + + /// @notice Restricts access to only the contract owner + modifier onlyOwner() { + require(msg.sender == owner, "Only owner"); + _; + } + + /// @notice Emitted when a new player joins the lottery + /// @param player Address of the player that joined + event PlayerJoined(address indexed player); + + /// @notice Emitted when a winner is selected + /// @param winner Address of the winner + event WinnerSelected(address indexed winner); + + /// @notice Emitted when a refund is issued to players + event RefundIssued(); + + /// @notice Initializes the lottery contract with a timeout block + /// @param _timeoutBlock The block number after which refund can be triggered + constructor(uint _timeoutBlock) { + owner = msg.sender; + timeoutBlock = _timeoutBlock; + } + + /// @notice Enter the lottery by paying exactly 0.01 ether + /// @dev Reverts if already joined, if fee is wrong, or if lottery is full + function enter() external payable { + require(!winnerSelected, "Game finished"); + require(playerCount < 3, "Lottery full"); + require(msg.value == ENTRY_FEE, "Deposit exact fee"); + + // Prevent duplicate entries + for (uint i = 0; i < playerCount; i++) { + require(players[i] != msg.sender, "Already joined"); + } + + // Add new player + players[playerCount] = payable(msg.sender); + playerCount++; + + emit PlayerJoined(msg.sender); + + // If 3 players have joined, pick a winner + if (playerCount == 3) { + _pickWinner(); + } + } + + /// @notice Picks a random winner once 3 players have entered + /// @dev Uses blockhash for pseudo-randomness (not secure for real money) + function _pickWinner() internal { + require(playerCount == 3, "Not enough players"); + + // Pick random index (0, 1, or 2) + uint randomIndex = uint(blockhash(block.number - 1)) % 3; + winner = players[randomIndex]; + winnerSelected = true; + + emit WinnerSelected(winner); + + _payout(); + } + + /// @notice Transfers the entire contract balance to the winner + function _payout() internal { + payable(winner).transfer(address(this).balance); + } + + /// @notice Refunds players if the lottery did not finish before the timeout + /// @dev Only the owner can call this, after `timeoutBlock` + function refund() external onlyOwner { + require(!winnerSelected, "Winner already selected"); + require(block.number >= timeoutBlock, "Timeout not reached yet"); + + // Refund all entered players + for (uint i = 0; i < playerCount; i++) { + players[i].transfer(ENTRY_FEE); + } + playerCount = 0; + + emit RefundIssued(); + } +} diff --git a/CharityDonation/Readme.md b/CharityDonation/Readme.md new file mode 100644 index 0000000..4b4c5f9 --- /dev/null +++ b/CharityDonation/Readme.md @@ -0,0 +1,116 @@ +# CharityDonationPool + +## Contract Name + +**CharityDonationPool** + +--- + +## Overview + +The `CharityDonationPool` contract acts as a transparent donation pool for charity projects. Donors can contribute Ether, and the contract owner can allocate funds to beneficiaries. The contract keeps track of individual donations and the total donated amount. + +This contract is ideal for fundraising prototypes, community-driven donation platforms, and learning owner-controlled fund allocation in Solidity. + +--- + +## Prerequisites + +* MetaMask or QSafe (for testnet deployment) +* Testnet ETH or QRN +* Access to QRemix IDE (or any Solidity dev environment) +* Basic knowledge of Solidity and fund management + +--- + +## Contract Functions + +### Constructor + +```solidity +constructor() +``` + +* **Purpose:** Initializes the contract and sets the deployer as the owner. +* **Access:** Deployment-only. + +--- + +### donate + +```solidity +donate() payable +``` + +* **Purpose:** Accepts Ether donations from any address. +* **Access:** Public, payable. +* **Condition:** `msg.value > 0`. +* **Emits:** `Donated` event. + +--- + +### allocate + +```solidity +allocate(address payable beneficiary, uint256 amount) +``` + +* **Purpose:** Sends a specified amount to a beneficiary. +* **Access:** Only contract owner. +* **Condition:** `amount <= contract balance`. +* **Emits:** `Allocated` event. + +--- + +### getBalance + +```solidity +getBalance() → uint256 +``` + +* **Purpose:** Returns the total Ether balance held in the contract. +* **Access:** Public view. + +--- + +## Access Control + +* **Owner (Deployer):** Can allocate funds to beneficiaries. +* **Donors:** Can contribute ETH but cannot control fund allocation. + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup + +* Open [qremix.org](https://qremix.org) +* Create folder: `CharityDonationPool/` +* Add `CharityDonationPool.sol` and paste the contract code. + +### Step 2: Compile + +* Go to Solidity Compiler +* Select version `0.8.19` (or compatible) +* Compile the contract + +### Step 3: Deploy + +* Deploy directly (no constructor args). + +### Step 4: Testing Flow + +1. Donor calls `donate()` sending ETH. +2. Owner calls `getBalance()` to check pool balance. +3. Owner calls `allocate(beneficiaryAddress, amount)` to transfer funds. +4. Beneficiary receives funds. + +--- + +## Security Notes & Recommendations + +* Contract uses `transfer`, which forwards limited gas. For more complex beneficiary contracts, switch to `call`. +* Centralized allocation by owner — for production, replace with DAO or multisig for decentralized governance. +* Add donation caps, minimums, or recurring donation logic as enhancements. + + diff --git a/CharityDonation/donation.sol b/CharityDonation/donation.sol new file mode 100644 index 0000000..321e1ff --- /dev/null +++ b/CharityDonation/donation.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier:MIT +pragma solidity ^0.8.19; + +/// @title CharityDonationPool - Donation pool for charity projects +/// @notice Donors can contribute, and owner allocates funds to beneficiaries +/// @dev Tracks total donations and allows transparent withdrawals +contract CharityDonationPool { + address public owner; + uint256 public totalDonations; + + mapping(address => uint256) public donations; + + event Donated(address indexed donor, uint256 amount); + event Allocated(address indexed beneficiary, uint256 amount); + + modifier onlyOwner() { + require(msg.sender == owner, "Not owner"); + _; + } + + constructor() { + owner = msg.sender; + } + + /// @notice Donate Ether to the charity pool + function donate() external payable { + require(msg.value > 0, "No donation sent"); + + donations[msg.sender] += msg.value; + totalDonations += msg.value; + + emit Donated(msg.sender, msg.value); + } + + /// @notice Allocate funds to a beneficiary + /// @param beneficiary Address to receive funds + /// @param amount Amount to send in wei + function allocate(address payable beneficiary, uint256 amount) external onlyOwner { + require(amount <= address(this).balance, "Insufficient balance"); + + beneficiary.transfer(amount); + emit Allocated(beneficiary, amount); + } + + /// @notice Get contract balance + function getBalance() external view returns (uint256) { + return address(this).balance; + } +} diff --git a/Decentralized-TalentShow/Readme.md b/Decentralized-TalentShow/Readme.md new file mode 100644 index 0000000..ac6bcd4 --- /dev/null +++ b/Decentralized-TalentShow/Readme.md @@ -0,0 +1,172 @@ +# DecentralizedTalentShow + +## Contract Name + +**DecentralizedTalentShow** + +--- + +## Overview + +The `DecentralizedTalentShow` contract runs multiple talent contests where creators submit entries (IPFS hashes) and the community votes by sending ETH (micro-fees). Each contest has a start and end block. Votes accumulate in the contest `prizePool`. After the contest ends, anyone can `finalizeContest` — the highest-voted submission receives the prize pool minus a platform fee (owner fee). This contract is great for experimenting with pay-to-vote mechanics, on-chain prize distribution, and contest lifecycle management. + +--- + +## Prerequisites + +* MetaMask (or other web3 wallet) +* Testnet ETH +* Access to QRemix IDE (or any Solidity environment) +* Familiarity with block numbers vs timestamps and IPFS usage + +--- + +## Contract Functions + +### Constructor + +```solidity +constructor(uint256 _platformFeeBps) +``` + +* **Purpose:** Set initial platform fee (basis points) and owner. +* **Access:** Called at deployment. + +--- + +### createContest + +```solidity +createContest(string title, uint256 startBlock, uint256 endBlock) → uint256 +``` + +* **Purpose:** Owner creates a new contest with timeframe. +* **Access:** Only owner. +* **Returns:** contestId (uint256) +* **Emits:** `ContestCreated` + +--- + +### submitEntry + +```solidity +submitEntry(uint256 contestId, string ipfsHash) +``` + +* **Purpose:** Submit an entry (IPFS content pointer) during contest active window. +* **Access:** Any address (creator). +* **Emits:** `SubmissionAdded` + +--- + +### vote + +```solidity +vote(uint256 contestId, uint256 submissionId) payable +``` + +* **Purpose:** Vote for a submission by paying ETH. Vote weight equals `msg.value`. +* **Access:** Any address. +* **Conditions:** Each address can vote **once per submission** (but may vote for other submissions). +* **Emits:** `Voted` + +--- + +### finalizeContest + +```solidity +finalizeContest(uint256 contestId) +``` + +* **Purpose:** Finalize contest after `endBlock`. Winner is the submission with highest `votes`. Prize distribution: `payout = prizePool - ownerFee`. Platform owner receives `ownerFee`. +* **Access:** Anyone (after contest ends). +* **Emits:** `ContestFinalized` + +--- + +### getContest + +```solidity +getContest(uint256 contestId) → (title, startBlock, endBlock, submissionCount, prizePool, finalized, winningSubmissionId) +``` + +* **Purpose:** Get contest metadata and status. + +--- + +### getSubmission + +```solidity +getSubmission(uint256 contestId, uint256 submissionId) → (creator, ipfsHash, votes, active) +``` + +* **Purpose:** Get submission metadata. + +--- + +### setPlatformFee + +```solidity +setPlatformFee(uint256 bps) +``` + +* **Purpose:** Update platform fee (basis points). +* **Access:** Only owner. +* **Condition:** Max enforced (e.g., <= 2000 bps = 20%). + +--- + +### ownerWithdraw + +```solidity +ownerWithdraw(uint256 amount) +``` + +* **Purpose:** Withdraw stuck or leftover ETH. +* **Access:** Only owner. + +--- + +## Access Control + +* **Owner (Deployer):** Create contests, change platform fee, withdraw stuck funds. +* **Creators:** Submit entries to contests within active windows. +* **Voters:** Vote by sending ETH and are prevented from double-voting the same submission. + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup + +* Open [qremix.org](https://qremix.org) +* Create folder: `DecentralizedTalentShow/` +* Add `DecentralizedTalentShow.sol` and paste the contract code. + +### Step 2: Compile + +* Go to Solidity Compiler +* Select version `0.8.19` (or compatible) +* Compile the contract + +### Step 3: Deploy + +* Deploy with arg `platformFeeBps` (e.g., `500` for 5%) + +### Step 4: Testing Flow + +1. Owner calls `createContest("Summer Jam", startBlock, endBlock)`. +2. Users call `submitEntry(contestId, "")` during active window. +3. Voters call `vote(contestId, submissionId)` sending small ETH amounts; each voter can vote once per submission. +4. After `endBlock`, anyone calls `finalizeContest(contestId)`. Winner receives prize pool minus owner fee; owner receives fee. + +--- + +## Security Notes & Recommendations + +* Double-vote prevention is per-submission per-address. If you want stricter controls (one vote per contest per address), change the mapping accordingly. +* Using `block.number` for time windows is deterministic but dependent on chain block time; optionally use `block.timestamp` if you prefer wall-clock times. +* `call` is used for transfers — watch for reentrancy. Functions that trigger external calls do not update critical state afterwards (checks-effects-interactions used). +* Gas: iterating submissions to find winners is O(n); be cautious of contests with many submissions. Consider off-chain tallying + merkle proof verification for very large contests. + + diff --git a/Decentralized-TalentShow/talentshow.sol b/Decentralized-TalentShow/talentshow.sol new file mode 100644 index 0000000..f4f2461 --- /dev/null +++ b/Decentralized-TalentShow/talentshow.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +/// @title DecentralizedTalentShow - Multi-contest voting DApp with pay-to-vote & prize pooling +/// @notice Create contests, submit entries (IPFS hash), let the community vote by paying a small fee. After deadline, finalize to pay winner with a platform fee. +/// @dev Uses pull/push payouts. Prevents double-voting per submission per contest. Platform owner collects fee on finalize. +contract DecentralizedTalentShow { + address public owner; + uint256 public platformFeeBps; // basis points (e.g., 500 = 5%) + + struct Contest { + string title; + uint256 startBlock; + uint256 endBlock; + uint256 submissionCount; + uint256 prizePool; // accumulated from votes + bool finalized; + uint256 winningSubmissionId; + } + + struct Submission { + address creator; + string ipfsHash; // content pointer + uint256 votes; // total vote weight (sum of vote fees) + bool active; + } + + uint256 public nextContestId = 1; + + // contestId => Contest + mapping(uint256 => Contest) public contests; + + // contestId => submissionId => Submission + mapping(uint256 => mapping(uint256 => Submission)) public submissions; + + // contestId => submissionId => mapping(voter => bool) to prevent double voting per submission + mapping(uint256 => mapping(uint256 => mapping(address => bool))) public voted; + + // events + event ContestCreated(uint256 indexed contestId, string title, uint256 startBlock, uint256 endBlock); + event SubmissionAdded(uint256 indexed contestId, uint256 indexed submissionId, address indexed creator, string ipfsHash); + event Voted(uint256 indexed contestId, uint256 indexed submissionId, address indexed voter, uint256 amount); + event ContestFinalized(uint256 indexed contestId, uint256 indexed winningSubmissionId, uint256 payoutAmount, uint256 ownerFee); + + modifier onlyOwner() { + require(msg.sender == owner, "Only owner"); + _; + } + + constructor(uint256 _platformFeeBps) { + owner = msg.sender; + platformFeeBps = _platformFeeBps; + } + + /// @notice Create a new contest + /// @param title Contest title + /// @param startBlock Block when voting/submissions start + /// @param endBlock Block when contest ends (finalize allowed after this) + function createContest(string calldata title, uint256 startBlock, uint256 endBlock) external onlyOwner returns (uint256) { + require(endBlock > startBlock, "Invalid block range"); + uint256 id = nextContestId++; + contests[id] = Contest({ + title: title, + startBlock: startBlock, + endBlock: endBlock, + submissionCount: 0, + prizePool: 0, + finalized: false, + winningSubmissionId: 0 + }); + + emit ContestCreated(id, title, startBlock, endBlock); + return id; + } + + /// @notice Submit an entry for a contest + /// @param contestId Contest id + /// @param ipfsHash IPFS hash or content pointer + function submitEntry(uint256 contestId, string calldata ipfsHash) external { + Contest storage c = contests[contestId]; + require(block.number >= c.startBlock && block.number <= c.endBlock, "Contest not active"); + + uint256 sid = ++c.submissionCount; + submissions[contestId][sid] = Submission({ + creator: msg.sender, + ipfsHash: ipfsHash, + votes: 0, + active: true + }); + + emit SubmissionAdded(contestId, sid, msg.sender, ipfsHash); + } + + /// @notice Vote for a submission by paying a fee (vote weight equals msg.value) + /// @dev Prevents same address voting multiple times for the same submission. However, an address may vote for multiple different submissions. + /// @param contestId Contest id + /// @param submissionId Submission id + function vote(uint256 contestId, uint256 submissionId) external payable { + Contest storage c = contests[contestId]; + require(block.number >= c.startBlock && block.number <= c.endBlock, "Contest not active"); + require(msg.value > 0, "Vote requires ETH"); + require(submissions[contestId][submissionId].active, "Invalid submission"); + require(!voted[contestId][submissionId][msg.sender], "Already voted this submission"); + + // mark voter to prevent double-vote on same submission + voted[contestId][submissionId][msg.sender] = true; + + // increment votes (weight by ETH) + submissions[contestId][submissionId].votes += msg.value; + + // increase contest prize pool + c.prizePool += msg.value; + + emit Voted(contestId, submissionId, msg.sender, msg.value); + } + + /// @notice Finalize the contest after endBlock. Picks winner (highest votes) and distributes prize pool. + /// @dev Owner receives platform fee. Anyone may call finalize after endBlock. + /// @param contestId Contest id + function finalizeContest(uint256 contestId) external { + Contest storage c = contests[contestId]; + require(block.number > c.endBlock, "Contest not ended"); + require(!c.finalized, "Already finalized"); + + // find winning submission (highest votes) + uint256 bestId = 0; + uint256 bestVotes = 0; + for (uint256 i = 1; i <= c.submissionCount; i++) { + if (submissions[contestId][i].votes > bestVotes) { + bestVotes = submissions[contestId][i].votes; + bestId = i; + } + } + + // compute fee and payout + uint256 pool = c.prizePool; + uint256 ownerFee = (pool * platformFeeBps) / 10000; + uint256 payout = pool - ownerFee; + + if (bestId == 0) { + // No submissions or no votes — owner keeps fee (which equals pool here) + ownerFee = pool; + payout = 0; + } else { + // pay winner creator + _safeSend(payable(submissions[contestId][bestId].creator), payout); + } + + // pay owner fee + if (ownerFee > 0) { + _safeSend(payable(owner), ownerFee); + } + + c.finalized = true; + c.winningSubmissionId = bestId; + + emit ContestFinalized(contestId, bestId, payout, ownerFee); + } + + /// @dev Safe send using call + function _safeSend(address payable to, uint256 amount) internal { + if (amount == 0) return; + (bool ok, ) = to.call{value: amount}(""); + require(ok, "Transfer failed"); + } + + /// @notice Get contest info + function getContest(uint256 contestId) external view returns ( + string memory title, + uint256 startBlock, + uint256 endBlock, + uint256 submissionCount, + uint256 prizePool, + bool finalized, + uint256 winningSubmissionId + ) { + Contest storage c = contests[contestId]; + return ( + c.title, + c.startBlock, + c.endBlock, + c.submissionCount, + c.prizePool, + c.finalized, + c.winningSubmissionId + ); + } + + /// @notice Get submission info for a contest + function getSubmission(uint256 contestId, uint256 submissionId) external view returns ( + address creator, + string memory ipfsHash, + uint256 votes, + bool active + ) { + Submission storage s = submissions[contestId][submissionId]; + return (s.creator, s.ipfsHash, s.votes, s.active); + } + + /// @notice Owner can change platform fee (basis points) + function setPlatformFee(uint256 bps) external onlyOwner { + require(bps <= 2000, "Fee too high"); // max 20% + platformFeeBps = bps; + } + + /// @notice Owner withdraws any stuck ETH + function ownerWithdraw(uint256 amount) external onlyOwner { + require(amount <= address(this).balance, "Not enough balance"); + _safeSend(payable(owner), amount); + } +} diff --git a/Decentralized-ToDo/Readme.md b/Decentralized-ToDo/Readme.md new file mode 100644 index 0000000..a00a6a8 --- /dev/null +++ b/Decentralized-ToDo/Readme.md @@ -0,0 +1,104 @@ +# DecentralizedTodoList + +## Contract Name + +**DecentralizedTodoList** + +--- + +## Overview + +The `DecentralizedTodoList` contract allows users to create and manage personal to-do lists directly on-chain. Each user can add tasks with descriptions, mark them as completed, and fetch their full task list. + +This contract is ideal for learning how to work with dynamic data structures in Solidity (`structs` and `mappings`) and for building simple dApps where persistent personal data storage is required. + +--- + +## Prerequisites + +* MetaMask or QSafe (for testnet deployment) +* Testnet ETH or QRN +* Access to QRemix IDE (or any Solidity dev environment) +* Basic knowledge of Solidity, structs, and arrays + +--- + +## Contract Functions + +### createTask + +```solidity +createTask(string description) +``` + +* **Purpose:** Add a new task to the sender’s personal task list. +* **Access:** Any address. +* **Emits:** `TaskCreated` event. + +--- + +### completeTask + +```solidity +completeTask(uint taskId) +``` + +* **Purpose:** Mark a task as completed. +* **Access:** Only the task owner. +* **Condition:** Task must exist and not already be completed. +* **Emits:** `TaskCompleted` event. + +--- + +### getMyTasks + +```solidity +getMyTasks() → Task[] +``` + +* **Purpose:** Retrieve the full list of tasks (description + completion status) for the caller. +* **Access:** Only the task owner. + +--- + +## Access Control + +* **Users:** Each address can manage their own tasks independently. +* **No Owner Role:** The contract is fully self-service; no admin or central authority. + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup + +* Open [qremix.org](https://qremix.org) +* Create folder: `DecentralizedTodoList/` +* Add `DecentralizedTodoList.sol` and paste the contract code. + +### Step 2: Compile + +* Go to Solidity Compiler +* Select version `0.8.19` (or compatible) +* Compile the contract + +### Step 3: Deploy + +* Deploy directly (no constructor args). + +### Step 4: Testing Flow + +1. Call `createTask("Buy groceries")`. +2. Call `createTask("Finish Solidity project")`. +3. Call `getMyTasks()` to fetch tasks. +4. Call `completeTask(0)` to mark the first task as completed. +5. Call `getMyTasks()` again to verify completion. + +--- + +## Security Notes & Recommendations + +* Each user only manages their own tasks (isolated mapping). +* No access for others to modify another user’s tasks. +* Be aware that storing large strings on-chain is costly — use short descriptions. +* For real-world apps, consider off-chain storage (IPFS/Arweave) with on-chain task references. diff --git a/Decentralized-ToDo/de-to-do.sol b/Decentralized-ToDo/de-to-do.sol new file mode 100644 index 0000000..c7be74a --- /dev/null +++ b/Decentralized-ToDo/de-to-do.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier:MIT +pragma solidity ^0.8.19; + +/// @title DecentralizedTodoList - On-chain to-do manager +/// @notice Users can create and complete tasks on-chain +/// @dev Stores user-specific task lists in mappings +contract DecentralizedTodoList { + struct Task { + string description; + bool completed; + } + + mapping(address => Task[]) private tasks; + + event TaskCreated(address indexed user, uint256 taskId, string description); + event TaskCompleted(address indexed user, uint256 taskId); + + /// @notice Add a new task to sender's list + /// @param description Description of the task + function createTask(string calldata description) external { + tasks[msg.sender].push(Task(description, false)); + emit TaskCreated(msg.sender, tasks[msg.sender].length - 1, description); + } + + /// @notice Mark a task as completed + /// @param taskId Index of the task to mark complete + function completeTask(uint256 taskId) external { + require(taskId < tasks[msg.sender].length, "Invalid task ID"); + require(!tasks[msg.sender][taskId].completed, "Already completed"); + + tasks[msg.sender][taskId].completed = true; + emit TaskCompleted(msg.sender, taskId); + } + + /// @notice Get all tasks of sender + /// @return Array of Task structs + function getMyTasks() external view returns (Task[] memory) { + return tasks[msg.sender]; + } +} diff --git a/SkillEndorsement/Readme.md b/SkillEndorsement/Readme.md new file mode 100644 index 0000000..9ac2c23 --- /dev/null +++ b/SkillEndorsement/Readme.md @@ -0,0 +1,167 @@ +# SkillEndorsement + +## Contract Name + +**SkillEndorsement** + +--- + +## Overview + +The `SkillEndorsement` contract allows on-chain skill claims where users stake ETH to claim a skill (e.g., "solidity" or "ux"). Other users can `endorse` a claim by staking ETH (which increases the claim's reputation) or `challenge` it by staking ETH (which reduces reputation). The contract owner acts as an arbitrator and resolves claims — funds are distributed according to the resolution outcome. + +This contract teaches multi-party staking flows, reputation as on-chain escrow, and pull-pattern distributions for pro-rata payouts. It’s suitable for prototyping decentralized reputation or portfolio systems. + +--- + +## Prerequisites + +* MetaMask (or other web3 wallet) +* Testnet ETH +* Access to QRemix IDE (or similar Solidity dev environment) +* Basic knowledge of Solidity, mappings, and fallback/pull payout patterns + +--- + +## Contract Functions + +### Constructor + +```solidity +constructor(uint256 _minClaimStake, uint256 _minEndorseStake, uint256 _minChallengeStake) +``` + +* **Purpose:** Initialize contract with minimum stake thresholds and set deployer as owner. +* **Access:** Called on deployment. + +--- + +### claimSkill + +```solidity +claimSkill(string skill) payable → uint256 +``` + +* **Purpose:** Create a new skill claim by staking ETH. +* **Access:** Any address. +* **Conditions:** `msg.value >= minClaimStake` +* **Returns:** `claimId` (uint256) +* **Emits:** `ClaimCreated` + +--- + +### endorse + +```solidity +endorse(uint256 claimId) payable +``` + +* **Purpose:** Endorse an existing claim by staking ETH (increases endorsements). +* **Access:** Any address. +* **Conditions:** `msg.value >= minEndorseStake` +* **Emits:** `Endorsed` + +--- + +### challenge + +```solidity +challenge(uint256 claimId) payable +``` + +* **Purpose:** Challenge an existing claim by staking ETH (increases challenges). +* **Access:** Any address. +* **Conditions:** `msg.value >= minChallengeStake` +* **Emits:** `Challenged` + +--- + +### resolveClaim + +```solidity +resolveClaim(uint256 claimId, bool validated) +``` + +* **Purpose:** Owner/arbitrator resolves an active claim (valid or invalid) and triggers distribution logic. +* **Access:** Only contract owner. +* **Notes:** Distributes endorsements/challenges/claimer stake according to example policy. +* **Emits:** `ClaimResolved` + +--- + +### withdrawEndorserShare + +```solidity +withdrawEndorserShare(uint256 claimId) +``` + +* **Purpose:** Endorsers claim their pro-rata payout after resolution. +* **Access:** Endorsers who contributed to claim. + +--- + +### withdrawChallengerShare + +```solidity +withdrawChallengerShare(uint256 claimId) +``` + +* **Purpose:** Challengers claim their pro-rata payout after rejection resolution. +* **Access:** Challengers who contributed to claim. + +--- + +### ownerWithdraw + +```solidity +ownerWithdraw(uint256 amount) +``` + +* **Purpose:** Owner withdraws accumulated platform fees kept in contract. +* **Access:** Only owner. + +--- + +## Access Control + +* **Owner (Deployer):** Arbitration power (resolve claims), can withdraw platform fees. +* **Claimers:** Start claims by staking ETH. +* **Endorsers / Challengers:** Stake ETH to endorse/challenge. Use pull-pattern to withdraw pro-rata shares after resolution. + +--- + +## Deployment & Testing on QRemix + +### Step 1: Setup + +* Open [qremix.org](https://qremix.org) +* Create folder: `SkillEndorsement/` +* Add `SkillEndorsement.sol` and paste the contract code. + +### Step 2: Compile + +* Go to Solidity Compiler +* Select version `0.8.19` (or compatible) +* Compile the contract + +### Step 3: Deploy + +* Deploy with constructor args (min stakes as wei), e.g., `10000000000000000` (0.01 ETH) etc. + +### Step 4: Testing Flow + +1. Alice calls `claimSkill("solidity")` with `msg.value = minClaimStake`. Note `claimId`. +2. Bob calls `endorse(claimId)` with `msg.value >= minEndorseStake`. +3. Carol calls `challenge(claimId)` with `msg.value >= minChallengeStake`. +4. Owner reviews evidence off-chain and calls `resolveClaim(claimId, true/false)`. +5. Endorsers/challengers call `withdrawEndorserShare` or `withdrawChallengerShare` respectively to claim their pro-rata share. + +--- + +## Security Notes & Recommendations + +* Owner-based arbitration is centralized; replace with multisig/DAO in production. +* To make distribution transparent and gas-efficient for large contributor sets, consider an ERC20 reward token or off-chain merkle payout with on-chain claim. +* Carefully test edge cases: zero endorsers/challengers, re-entrancy (pull pattern used), rounding issues in pro-rata arithmetic. +* Consider adding evidence storage (IPFS hash) for claims and disputes. + diff --git a/SkillEndorsement/skill-endorse.sol b/SkillEndorsement/skill-endorse.sol new file mode 100644 index 0000000..dddd3f3 --- /dev/null +++ b/SkillEndorsement/skill-endorse.sol @@ -0,0 +1,248 @@ +//SPDX-License-Identifier:MIT +pragma solidity ^0.8.19; + +/// @title SkillEndorsement - Stake-based skill claims with endorsements & challenges +/// @notice Users can claim a skill by staking ETH; others can endorse or challenge by staking ETH. +/// The contract owner (arbitrator) resolves claims; funds are distributed based on resolution. +/// @dev This is a demonstration contract. Owner-based arbitration is centralized — replace with multisig/DAO for production. +contract SkillEndorsement { + address public owner; + uint256 public minClaimStake; // minimum stake to claim a skill + uint256 public minEndorseStake; // minimum stake to endorse + uint256 public minChallengeStake; // minimum stake to challenge + + struct Claim { + address claimer; + string skill; + uint256 stake; // stake by claimer + uint256 endorsements; // total value endorsed + uint256 challenges; // total value challenged + bool active; // active until resolved + bool validated; // result set after resolution + uint256 createdAt; + } + + // claimId counter + uint256 public nextClaimId; + + // claimId => Claim + mapping(uint256 => Claim) public claims; + + // claimId => endorser => amount + mapping(uint256 => mapping(address => uint256)) public endorseAmount; + + // claimId => challenger => amount + mapping(uint256 => mapping(address => uint256)) public challengeAmount; + + // events + event ClaimCreated(uint256 indexed claimId, address indexed claimer, string skill, uint256 stake); + event Endorsed(uint256 indexed claimId, address indexed endorser, uint256 amount); + event Challenged(uint256 indexed claimId, address indexed challenger, uint256 amount); + event ClaimResolved(uint256 indexed claimId, bool validated); + + modifier onlyOwner() { + require(msg.sender == owner, "Only owner"); + _; + } + + constructor(uint256 _minClaimStake, uint256 _minEndorseStake, uint256 _minChallengeStake) { + owner = msg.sender; + minClaimStake = _minClaimStake; + minEndorseStake = _minEndorseStake; + minChallengeStake = _minChallengeStake; + nextClaimId = 1; + } + + /// @notice Claim a skill by staking ETH (creates a new claim) + /// @dev `msg.value` must be >= minClaimStake and claim for same skill by same claimer must not be active + /// @param skill Short string identifier for the skill (e.g., "solidity", "react") + function claimSkill(string calldata skill) external payable returns (uint256) { + require(msg.value >= minClaimStake, "Stake too small"); + + // ensure claimer doesn't have an active claim for the same skill + // linear scan is avoided; instead we allow multiple claims but only track per claimId (simpler) + uint256 id = nextClaimId++; + claims[id] = Claim({ + claimer: msg.sender, + skill: skill, + stake: msg.value, + endorsements: 0, + challenges: 0, + active: true, + validated: false, + createdAt: block.timestamp + }); + + emit ClaimCreated(id, msg.sender, skill, msg.value); + return id; + } + + /// @notice Endorse a claim by staking ETH (endorser's stake increases claim reputation) + /// @dev msg.value must be >= minEndorseStake + /// @param claimId The claim to endorse + function endorse(uint256 claimId) external payable { + Claim storage c = claims[claimId]; + require(c.active, "Claim not active"); + require(msg.value >= minEndorseStake, "Endorse stake too small"); + + endorseAmount[claimId][msg.sender] += msg.value; + c.endorsements += msg.value; + + emit Endorsed(claimId, msg.sender, msg.value); + } + + /// @notice Challenge a claim by staking ETH (reduces claim reputation) + /// @dev msg.value must be >= minChallengeStake + /// @param claimId The claim to challenge + function challenge(uint256 claimId) external payable { + Claim storage c = claims[claimId]; + require(c.active, "Claim not active"); + require(msg.value >= minChallengeStake, "Challenge stake too small"); + + challengeAmount[claimId][msg.sender] += msg.value; + c.challenges += msg.value; + + emit Challenged(claimId, msg.sender, msg.value); + } + + /// @notice Resolve a claim (owner/arbitrator decides outcome). Distributes funds based on result. + /// @dev Distribution policy (example): if validated -> claimer gets stake + 80% endorsements; endorsers get 20% back pro rata. + /// if rejected -> challengers get 80% of endorsements pro rata + their stakes; claimer's stake is slashed (used for challengers). + /// @param claimId Claim id to resolve + /// @param validated Boolean result (true = claim valid) + function resolveClaim(uint256 claimId, bool validated) external onlyOwner { + Claim storage c = claims[claimId]; + require(c.active, "Claim not active"); + + c.active = false; + c.validated = validated; + + // compute totals + uint256 totalEndorse = c.endorsements; + uint256 totalChallenge = c.challenges; + uint256 claimerStake = c.stake; + + if (validated) { + // Claimer rewarded: stake + 80% of endorsements + (optionally) a small bonus from challenged stakes + uint256 endorsersShare = (totalEndorse * 20) / 100; // 20% returned to endorsers collectively + uint256 claimerShareFromEndorse = totalEndorse - endorsersShare; // 80% + uint256 payoutToClaimer = claimerStake + claimerShareFromEndorse; + + // pay claimer + _safeSend(payable(c.claimer), payoutToClaimer); + + // distribute endorsersShare back to endorsers proportionally + if (endorsersShare > 0) { + _distributeToEndorsers(claimId, endorsersShare); + } + + // challengers lose their stakes (remain in contract as fee) - for simplicity fee stays with contract owner as platform fee + // optionally owner can withdraw accumulated fees later (not implemented) + } else { + // Claim rejected: challengers get reward from endorsements and claimer's stake is slashed. + // Pay challengers: they receive their original stake back + proportional share of endorsements + portion of claimer's stake. + uint256 challengersRewardPool = totalEndorse + claimerStake; // endorsements + claimer stake available for challengers + + if (totalChallenge > 0) { + // distribute challengersRewardPool proportionally to challengers' stake + _distributeToChallengers(claimId, challengersRewardPool); + } else { + // No challengers but rejected — send entire pool to owner (platform) + // Note: this is a fallback; in practice owner shouldn't reject without challengers + _safeSend(payable(owner), challengersRewardPool); + } + + // endorsers lose endorsements (their stakes are used for challenger payouts) + } + + emit ClaimResolved(claimId, validated); + } + + /// @dev Internal helper to distribute `amount` proportionally among endorsers of a claim + function _distributeToEndorsers(uint256 claimId, uint256 amount) internal { + Claim storage c = claims[claimId]; + uint256 total = c.endorsements; + if (total == 0 || amount == 0) return; + + // iterate endorsers — we cannot iterate mappings. To keep contract gas-bounded, + // this implementation requires know-how of endorsers or off-chain reconciliation. + // For demonstration, we iterate via a naive approach is impossible; so we implement a pull pattern: + // store `endorserReturn[claimId]` as total share per unit endorsement (scaled) + // then endorsers can call withdrawEndorseerShare to claim their pro-rata share. + // Implementation for pro-rata withdraw: + uint256 ratePerWei = (amount * 1e18) / total; // scaled rate + endorserRate[claimId] = ratePerWei; + } + + /// @dev Internal helper to distribute `amount` proportionally among challengers + function _distributeToChallengers(uint256 claimId, uint256 amount) internal { + Claim storage c = claims[claimId]; + uint256 total = c.challenges; + if (total == 0 || amount == 0) return; + + uint256 ratePerWei = (amount * 1e18) / total; // scaled + challengerRate[claimId] = ratePerWei; + } + + // Pull-pattern mappings for endorsers/challengers to claim pro-rata share after resolution + mapping(uint256 => uint256) public endorserRate; // scaled amount per wei endorsed (1e18) + mapping(uint256 => uint256) public challengerRate; // scaled amount per wei challenged (1e18) + + /// @notice Endorsers withdraw their pro-rata share after claim resolution (if any) + /// @param claimId Claim id + function withdrawEndorserShare(uint256 claimId) external { + Claim storage c = claims[claimId]; + require(!c.active, "Claim still active"); + uint256 endorsed = endorseAmount[claimId][msg.sender]; + require(endorsed > 0, "No endorsement stake recorded"); + + uint256 rate = endorserRate[claimId]; + require(rate > 0, "No payout available"); + + // compute payout + uint256 payout = (endorsed * rate) / 1e18; + + // zero out to avoid re-entrancy double-claim + endorseAmount[claimId][msg.sender] = 0; + _safeSend(payable(msg.sender), payout); + } + + /// @notice Challengers withdraw their share after claim rejection + /// @param claimId Claim id + function withdrawChallengerShare(uint256 claimId) external { + Claim storage c = claims[claimId]; + require(!c.active, "Claim still active"); + uint256 challenged = challengeAmount[claimId][msg.sender]; + require(challenged > 0, "No challenge stake recorded"); + + uint256 rate = challengerRate[claimId]; + require(rate > 0, "No payout available"); + + uint256 payout = (challenged * rate) / 1e18; + + // zero out + challengeAmount[claimId][msg.sender] = 0; + _safeSend(payable(msg.sender), payout); + } + + /// @notice Owner can withdraw accumulated platform fees (ETH left in contract that are not owed) + function ownerWithdraw(uint256 amount) external onlyOwner { + require(amount <= address(this).balance, "Insufficient balance"); + _safeSend(payable(owner), amount); + } + + /// @dev Safe send wrapper using call + function _safeSend(address payable to, uint256 amount) internal { + if (amount == 0) return; + (bool ok, ) = to.call{value: amount}(""); + require(ok, "Transfer failed"); + } + + /// @notice View function to get claim score (endorse - challenge) + /// @param claimId Claim id + /// @return score (signed int: endorsements - challenges) + function claimScore(uint256 claimId) external view returns (int256) { + Claim storage c = claims[claimId]; + return int256(c.endorsements) - int256(c.challenges); + } +}