-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b015a18
commit 034871f
Showing
10 changed files
with
524 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
22.11.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
//SPDX-License-Identifier: MIT | ||
pragma solidity >=0.8.0 <0.9.0; | ||
|
||
// Useful for debugging. Remove when deploying to a live network. | ||
import "hardhat/console.sol"; | ||
|
||
// Use openzeppelin to inherit battle-tested implementations (ERC20, ERC721, etc) | ||
// import "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
/// @title YouSplit | ||
/// @author jcarbonnell (partage.xyz) | ||
/// @notice An ethereum smart contract to split YouTube royalties between a bunch of beneficiaries. | ||
|
||
contract YouSplit { | ||
// State Variables | ||
address public immutable owner; | ||
uint256 public totalShares; | ||
uint256 public totalBalance; | ||
|
||
struct Beneficiary { | ||
uint256 shares; | ||
uint256 withdrawn; | ||
bool isEligible; | ||
} | ||
|
||
mapping(address => Beneficiary) public beneficiaries; | ||
|
||
// Events: a way to emit log statements from smart contract that can be listened to by external parties | ||
event ContractFunded(address indexed sender, uint256 amount); | ||
event BeneficiaryAdded(address indexed beneficiary, uint256 shares); | ||
event BeneficiaryUpdated(address indexed beneficiary, uint256 newShares, bool newEligibility); | ||
event BeneficiaryDeleted(address indexed beneficiary); | ||
event Withdrawal(address indexed beneficiary, uint256 amount); | ||
|
||
// Constructor: Called once on contract deployment | ||
// Check packages/hardhat/deploy/00_deploy_your_contract.ts | ||
constructor(address[] memory _beneficiaries, uint256[] memory _shares) { | ||
require(_beneficiaries.length == _shares.length, "Must provide equal number of beneficiaries and shares."); | ||
owner = msg.sender; | ||
totalShares = 100; | ||
|
||
// Ensure the onwer gets 5% of the total shares | ||
uint256 ownerShares = 5; | ||
beneficiaries[owner] = Beneficiary({ | ||
shares: ownerShares, | ||
withdrawn: 0, | ||
isEligible: true | ||
}); | ||
emit BeneficiaryAdded(owner, ownerShares); | ||
|
||
// Add the rest of the beneficiaries | ||
uint256 remainingShares = 95; | ||
for (uint i = 0; i < _beneficiaries.length; i++) { | ||
uint256 sharePercentage = (_shares[i] * remainingShares) / 100; | ||
beneficiaries[_beneficiaries[i]] = Beneficiary({ | ||
shares: sharePercentage, | ||
withdrawn: 0, | ||
isEligible: true | ||
}); | ||
emit BeneficiaryAdded(_beneficiaries[i], sharePercentage); | ||
} | ||
} | ||
|
||
// Function to receive ETH | ||
receive() external payable { | ||
totalBalance += msg.value; | ||
emit ContractFunded(msg.sender, msg.value); | ||
} | ||
|
||
// Function to check balance and eligibility | ||
function getBeneficiaryInfo(address _beneficiary) public view returns (uint256 shares, uint256 withdrawn, uint256 eligibleAmount, bool isEligible) { | ||
Beneficiary memory beneficiary = beneficiaries[_beneficiary]; | ||
uint256 sharePercentage = beneficiary.shares * 100 / totalShares; | ||
return (beneficiary.shares, beneficiary.withdrawn, (totalBalance * sharePercentage) / 100, beneficiary.isEligible); | ||
} | ||
|
||
// Function to withdraw funds | ||
function withdraw() public { | ||
Beneficiary storage beneficiary = beneficiaries[msg.sender]; | ||
require(beneficiary.isEligible, "You are not eligible to withdraw."); | ||
uint256 sharePercentage = beneficiary.shares * 100 / totalShares; | ||
uint256 amount = (totalBalance * sharePercentage) / 100 - beneficiary.withdrawn; | ||
|
||
require(amount > 0, "You have no funds to withdraw."); | ||
beneficiary.withdrawn += amount; | ||
totalBalance -= amount; | ||
payable(msg.sender).transfer(amount); | ||
emit Withdrawal(msg.sender, amount); | ||
} | ||
|
||
// Function to add or update beneficiary | ||
function setBeneficiary(address _beneficiary, uint256 _shares, bool _isEligible) public { | ||
require(msg.sender == owner, "Only the owner can add or update beneficiaries."); | ||
if (_beneficiary != owner) { | ||
if(beneficiaries[_beneficiary].shares > 0) { | ||
totalShares -= beneficiaries[_beneficiary].shares; | ||
} | ||
beneficiaries[_beneficiary] = Beneficiary({ | ||
shares: _shares, | ||
withdrawn: 0, | ||
isEligible: _isEligible | ||
}); | ||
totalShares += _shares; | ||
emit BeneficiaryAdded(_beneficiary, _shares); | ||
} | ||
} | ||
|
||
// Function to remove beneficiary | ||
function removeBeneficiary(address _beneficiary) public { | ||
require(msg.sender == owner, "Only the owner can remove beneficiaries."); | ||
require(_beneficiary != owner, "You cannot remove the owner."); | ||
totalShares -= beneficiaries[_beneficiary].shares; | ||
delete beneficiaries[_beneficiary]; | ||
emit BeneficiaryDeleted(_beneficiary); | ||
} | ||
|
||
// Function to get total funds in the contract | ||
function getTotalFunds() public view returns (uint256) { | ||
return address(this).balance; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { expect } from "chai"; | ||
import { ethers } from "hardhat"; | ||
import { YouSplit } from "../typechain-types/YouSplit"; | ||
import { YouSplit__factory } from "../typechain-types/factories/YouSplit__factory"; | ||
|
||
describe("YouSplit", function () { | ||
let youSplit: YouSplit; | ||
let owner: any; | ||
let beneficiary1: any; | ||
let beneficiary2: any; | ||
|
||
before(async () => { | ||
// signers for different roles in the contract | ||
const signers = await ethers.getSigners(); | ||
owner = signers[0]; | ||
beneficiary1 = signers[1]; | ||
beneficiary2 = signers[2]; | ||
|
||
// deploy the contract | ||
const youSplitFactory = await ethers.getContractFactory("YouSplit"); | ||
youSplit = await youSplitFactory.deploy([beneficiary1.address, beneficiary2.address],[475, 475]); | ||
await youSplit.waitForDeployment(); | ||
}); | ||
|
||
describe("Deployment", function () { | ||
it("Should set the correct owner", async function () { | ||
expect(await youSplit.owner()).to.equal(owner.address); | ||
}); | ||
|
||
it("Should initialize with correct share distribution", async function () { | ||
// Check owner shares | ||
const { shares: ownerShares } = await youSplit.beneficiaries(owner.address); | ||
expect(ownerShares).to.equal(5); // Owner should have 5% of shares | ||
|
||
// Check beneficiary shares | ||
const { shares: beneficiary1Shares } = await youSplit.beneficiaries(beneficiary1.address); | ||
expect(beneficiary1Shares).to.equal(475); // 95% / 2 = 47.5% (represented as 475 out of 10000) | ||
|
||
const { shares: beneficiary2Shares } = await youSplit.beneficiaries(beneficiary2.address); | ||
expect(beneficiary2Shares).to.equal(475); | ||
}); | ||
|
||
it("Should start with zero withdrawn for all beneficiaries", async function () { | ||
expect((await youSplit.beneficiaries(owner.address)).withdrawn).to.equal(0); | ||
expect((await youSplit.beneficiaries(beneficiary1.address)).withdrawn).to.equal(0); | ||
expect((await youSplit.beneficiaries(beneficiary2.address)).withdrawn).to.equal(0); | ||
}); | ||
|
||
it("Should set all beneficiaries as eligible", async function () { | ||
expect((await youSplit.beneficiaries(owner.address)).isEligible).to.equal(true); | ||
expect((await youSplit.beneficiaries(beneficiary1.address)).isEligible).to.equal(true); | ||
expect((await youSplit.beneficiaries(beneficiary2.address)).isEligible).to.equal(true); | ||
}); | ||
|
||
it("Should start with zero balance", async function () { | ||
expect(await youSplit.totalBalance()).to.equal(0); | ||
}); | ||
|
||
it("Should allow funds to be sent to the contract", async function () { | ||
const amount = ethers.parseEther("1"); | ||
await owner.sendTransaction({ to: await youSplit.getAddress(), value: amount }); | ||
|
||
expect(await youSplit.totalBalance()).to.equal(amount); | ||
}); | ||
|
||
it("Should allow beneficiaries to withdraw their funds", async function () { | ||
const initialBalance = await ethers.provider.getBalance(beneficiary1.address); | ||
await youSplit.connect(beneficiary1).withdraw(); | ||
const afterWithdrawBalance = await ethers.provider.getBalance(beneficiary1.address); | ||
|
||
// Check if balance increased after withdrawal | ||
expect(afterWithdrawBalance > initialBalance).to.be.true; | ||
|
||
// Check the withdrawn amount for beneficiary1 | ||
const { withdrawn: withdrawnAmount } = await youSplit.beneficiaries(beneficiary1.address); | ||
expect(withdrawnAmount).to.be.gt(0); | ||
}); | ||
|
||
it("Should not allow non-eligible beneficiaries to withdraw", async function () { | ||
await youSplit.setBeneficiary(beneficiary2.address, 0, false); // Make beneficiary2 ineligible | ||
await expect(youSplit.connect(beneficiary2).withdraw()).to.be.revertedWith("You are not eligible to withdraw."); | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.