First off, thank you for considering contributing to PayNote! It's people like you that make PayNote such a great tool for the Ethereum community.
This project and everyone participating in it is governed by our commitment to fostering an open and welcoming environment. We pledge to make participation in our project a harassment-free experience for everyone.
Before creating bug reports, please check the existing issues as you might find that you don't need to create one. When you are creating a bug report, please include as many details as possible:
- Use a clear and descriptive title
- Describe the exact steps to reproduce the problem
- Provide specific examples to demonstrate the steps
- Describe the behavior you observed and what you expected
- Include screenshots if relevant
- Specify your environment (OS, Node version, Hardhat version, etc.)
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include:
- Use a clear and descriptive title
- Provide a detailed description of the suggested enhancement
- Explain why this enhancement would be useful
- List some examples of how it would be used
- Fork the repo and create your branch from
main - If you've added code that should be tested, add tests
- If you've changed APIs, update the documentation
- Ensure the test suite passes
- Make sure your code follows the existing style
- Issue the pull request!
# Clone your fork
git clone https://github.com/YOUR_USERNAME/PayNote.git
cd PayNote/contracts
# Install dependencies
npm install
# Compile contracts
npx hardhat compile
# Run tests
npx hardhat test-
Create a branch
git checkout -b feature/my-new-feature
-
Make your changes
- Write clean, readable code
- Follow the existing code style
- Add comments for complex logic
- Keep gas costs in mind
-
Test your changes
# Run all tests npx hardhat test # Run specific test file npx hardhat test test/PayNoteRegistry.ts
-
Commit your changes
git add . git commit -m "feat: add amazing feature"
We follow the Conventional Commits specification:
feat:- New featurefix:- Bug fixdocs:- Documentation changesstyle:- Code style changes (formatting, etc.)refactor:- Code refactoringtest:- Adding or updating testschore:- Maintenance tasks
Examples:
feat: add multi-recipient payment support
fix: resolve reentrancy vulnerability in withdrawal
docs: update deployment guide with mainnet instructions
test: add edge case tests for zero-value payments
Follow the Solidity Style Guide:
// ✅ Good
function sendPaymentWithReference(
address recipient,
string calldata payReference
) external payable returns (bytes32 payNoteId) {
require(recipient != address(0), "Invalid recipient");
// ...
}
// ❌ Bad
function sendPaymentWithReference(address recipient,string calldata payReference) external payable returns(bytes32 payNoteId){
require(recipient!=address(0),"Invalid recipient");
// ...
}// ✅ Good
const payNote = await payNoteRegistry.read.resolvePayNote([payNoteId]);
expect(payNote.sender).to.equal(sender.account.address);
// ❌ Bad
const payNote=await payNoteRegistry.read.resolvePayNote([payNoteId])
expect(payNote.sender).to.equal(sender.account.address)Always consider gas costs:
// ✅ Good - Pack variables
struct PayNote {
address sender; // 20 bytes
address recipient; // 20 bytes
uint256 amount; // 32 bytes
string payReference; // dynamic
uint256 timestamp; // 32 bytes
}
// ❌ Bad - Wasteful storage
mapping(bytes32 => address) public senders;
mapping(bytes32 => address) public recipients;
mapping(bytes32 => uint256) public amounts;
// ... (multiple mappings instead of struct)All code contributions must include tests:
// contracts/tests/MyFeature.t.sol
function test_MyNewFeature() public {
// Setup
address recipient = address(0x123);
// Execute
bytes32 id = payNoteRegistry.sendPaymentWithReference{value: 1 ether}(
recipient,
"Test"
);
// Assert
assertTrue(payNoteRegistry.payNoteExists(id));
}// test/MyFeature.ts
it("Should handle my new feature correctly", async function () {
const { payNoteRegistry, sender, recipient } = await deployFixture();
const hash = await payNoteRegistry.write.myNewFeature(
[recipient.account.address],
{ account: sender.account }
);
expect(hash).to.not.equal(undefined);
});Update documentation when you:
- Add new features
- Change existing behavior
- Fix bugs that affect usage
- Add configuration options
Documentation locations:
README.md- Overview and quick startcontracts/docs/- Detailed guides- Code comments - Complex logic explanation
- Function NatSpec - All public/external functions
DO NOT open a public issue for security vulnerabilities.
Instead:
- Email security@paynote.io
- Or open a private security advisory on GitHub
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
When contributing code:
- ✅ Use OpenZeppelin contracts when possible
- ✅ Add reentrancy guards for external calls
- ✅ Validate all inputs
- ✅ Check for integer overflow/underflow
- ✅ Use
callinstead oftransferfor ETH transfers - ✅ Follow Checks-Effects-Interactions pattern
- ❌ Never use
tx.originfor authentication - ❌ Avoid
delegatecallunless necessary - ❌ Don't rely on timestamps for critical logic
-
Automated Checks
- Tests must pass
- Code must compile
- Linting must pass
-
Code Review
- Maintainer reviews code
- Provides feedback
- Requests changes if needed
-
Approval & Merge
- Approved by maintainer
- Merged to main branch
- Included in next release
PayNote/
├── contracts/
│ ├── contracts/ # Solidity contracts
│ │ ├── PayNoteRegistry.sol
│ │ ├── interfaces/
│ │ └── tests/
│ ├── test/ # TypeScript tests
│ ├── scripts/ # Deployment scripts
│ ├── ignition/ # Ignition modules
│ └── docs/ # Documentation
└── frontend/ # Frontend (future)
Don't hesitate to ask! You can:
- Open a discussion on GitHub
- Join our Discord
- Email hello@paynote.io
Contributors will be:
- Listed in our README
- Mentioned in release notes
- Credited in documentation
- Given contributor role on Discord
By contributing, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to PayNote! 🎉