From dc9ea45f67b6a3ec5ac65c1e44d49f6111cf573f Mon Sep 17 00:00:00 2001 From: SmartFlow Developer Date: Sun, 28 Dec 2025 01:27:08 +0100 Subject: [PATCH 1/2] Add bulk operations support to CircleCare treasury - Implement add-multiple-expenses for bulk expense creation (up to 10 expenses per transaction) - Implement settle-multiple-debts for bulk debt settlement (up to 10 settlements per transaction) - Implement add-multiple-members for bulk member addition (up to 10 members per transaction) - Added comprehensive test suite covering all bulk operations - Tests include successful bulk operations, error handling, authorization checks, and edge cases - Bulk operations maintain all existing security validations and use restrict-assets? protection - Compatible with existing single-operation workflow while providing efficiency for care circles --- contracts/contracts/circle-treasury.clar | 103 ++++++ contracts/tests/circle-treasury.test.ts | 437 +++++++++++++++++++++++ 2 files changed, 540 insertions(+) diff --git a/contracts/contracts/circle-treasury.clar b/contracts/contracts/circle-treasury.clar index 6e24f17..12148e3 100644 --- a/contracts/contracts/circle-treasury.clar +++ b/contracts/contracts/circle-treasury.clar @@ -527,3 +527,106 @@ false ) ) + +;; Bulk Operations + +;; Add multiple expenses in one transaction (up to 10) +(define-private (add-expense-internal + (expense-data {description: (string-ascii 200), amount: uint, participants: (list 20 principal)})) + (let + ( + (circle-id (get circle-id expense-data)) + (description (get description expense-data)) + (amount (get amount expense-data)) + (participants (get participants expense-data)) + ) + ;; Call the main add-expense function + (add-expense circle-id description amount participants) + ) +) + +;; Add multiple expenses in one transaction +(define-public (add-multiple-expenses + (circle-id uint) + (expenses (list 10 {description: (string-ascii 200), amount: uint, participants: (list 20 principal)}))) + (let + ( + (circle (unwrap! (map-get? circles circle-id) ERR-CIRCLE-NOT-FOUND)) + (member-key {circle-id: circle-id, member: tx-sender}) + ) + ;; Basic validations + (asserts! (not (get paused circle)) ERR-CIRCLE-PAUSED) + (asserts! (get active (unwrap! (map-get? members member-key) ERR-MEMBER-NOT-FOUND)) ERR-UNAUTHORIZED) + (asserts! (> (len expenses) u0) ERR-INVALID-AMOUNT) + + ;; Process all expenses - each will validate individually + (ok (map add-expense-internal + (map (lambda (expense) (merge expense {circle-id: circle-id})) expenses))) + ) +) + +;; Settle multiple debts in one transaction (up to 10) +(define-private (settle-debt-internal (settlement-data {creditor: principal})) + (let + ( + (circle-id (get circle-id settlement-data)) + (creditor (get creditor settlement-data)) + ) + ;; Call the main settle function + (settle-debt-stx circle-id creditor) + ) +) + +;; Settle multiple debts in one transaction +(define-public (settle-multiple-debts + (circle-id uint) + (settlements (list 10 {creditor: principal}))) + (let + ( + (circle (unwrap! (map-get? circles circle-id) ERR-CIRCLE-NOT-FOUND)) + (member-key {circle-id: circle-id, member: tx-sender}) + ) + ;; Basic validations + (asserts! (not (get paused circle)) ERR-CIRCLE-PAUSED) + (asserts! (get active (unwrap! (map-get? members member-key) ERR-MEMBER-NOT-FOUND)) ERR-UNAUTHORIZED) + (asserts! (> (len settlements) u0) ERR-INVALID-AMOUNT) + + ;; Process all settlements - each will validate individually + (ok (map settle-debt-internal + (map (lambda (settlement) (merge settlement {circle-id: circle-id})) settlements))) + ) +) + +;; Bulk add members to circle (up to 10) +(define-private (add-member-internal + (member-data {member: principal, nickname: (string-ascii 32)})) + (let + ( + (circle-id (get circle-id member-data)) + (member (get member member-data)) + (nickname (get nickname member-data)) + ) + ;; Call the main add-member function + (add-member circle-id member nickname) + ) +) + +;; Add multiple members in one transaction +(define-public (add-multiple-members + (circle-id uint) + (new-members (list 10 {member: principal, nickname: (string-ascii 32)}))) + (let + ( + (circle (unwrap! (map-get? circles circle-id) ERR-CIRCLE-NOT-FOUND)) + ) + ;; Basic validations + (asserts! (is-eq tx-sender (get creator circle)) ERR-UNAUTHORIZED) + (asserts! (not (get paused circle)) ERR-CIRCLE-PAUSED) + (asserts! (> (len new-members) u0) ERR-INVALID-AMOUNT) + (asserts! (<= (+ (get member-count circle) (len new-members)) MAX-MEMBERS) ERR-MAX-MEMBERS) + + ;; Process all member additions - each will validate individually + (ok (map add-member-internal + (map (lambda (member-data) (merge member-data {circle-id: circle-id})) new-members))) + ) +) diff --git a/contracts/tests/circle-treasury.test.ts b/contracts/tests/circle-treasury.test.ts index 1ea8f01..fb75e56 100644 --- a/contracts/tests/circle-treasury.test.ts +++ b/contracts/tests/circle-treasury.test.ts @@ -696,4 +696,441 @@ describe("CircleCare Circle Treasury - Clarity 4 Tests", () => { expect(result).not.toBeNone(); }); }); + + describe("Bulk Operations", () => { + beforeEach(() => { + simnet.callPublicFn( + "circle-treasury", + "initialize-circle", + [ + Cl.uint(1), + Cl.stringAscii("Bulk Circle"), + Cl.principal(wallet1), + Cl.stringAscii("Creator") + ], + deployer + ); + + simnet.callPublicFn( + "circle-treasury", + "add-member", + [Cl.uint(1), Cl.principal(wallet2), Cl.stringAscii("Bob")], + wallet1 + ); + + simnet.callPublicFn( + "circle-treasury", + "add-member", + [Cl.uint(1), Cl.principal(wallet3), Cl.stringAscii("Charlie")], + wallet1 + ); + }); + + describe("Bulk Expense Addition", () => { + it("adds multiple expenses successfully", () => { + const expenses = [ + { + description: "Dinner", + amount: 300000000, // 300 STX + participants: [wallet1, wallet2, wallet3] + }, + { + description: "Lunch", + amount: 150000000, // 150 STX + participants: [wallet1, wallet2] + }, + { + description: "Coffee", + amount: 50000000, // 50 STX + participants: [wallet2, wallet3] + } + ]; + + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-expenses", + [ + Cl.uint(1), + Cl.list(expenses.map(exp => Cl.tuple({ + 'description': Cl.stringAscii(exp.description), + 'amount': Cl.uint(exp.amount), + 'participants': Cl.list(exp.participants.map(p => Cl.principal(p))) + }))) + ], + wallet1 + ); + + expect(result).toBeOk(Cl.list([ + Cl.uint(1), // First expense ID + Cl.uint(2), // Second expense ID + Cl.uint(3) // Third expense ID + ])); + }); + + it("validates expenses individually", () => { + const expenses = [ + { + description: "Valid", + amount: 100000000, + participants: [wallet1, wallet2] + }, + { + description: "Invalid Participants", + amount: 50000000, + participants: [wallet1, wallet4] // wallet4 not a member + } + ]; + + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-expenses", + [ + Cl.uint(1), + Cl.list(expenses.map(exp => Cl.tuple({ + 'description': Cl.stringAscii(exp.description), + 'amount': Cl.uint(exp.amount), + 'participants': Cl.list(exp.participants.map(p => Cl.principal(p))) + }))) + ], + wallet1 + ); + + expect(result).toBeErr(Cl.uint(202)); // ERR-INVALID-PARTICIPANT from second expense + }); + + it("requires active circle membership", () => { + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-expenses", + [ + Cl.uint(1), + Cl.list([Cl.tuple({ + 'description': Cl.stringAscii("Test"), + 'amount': Cl.uint(100000000), + 'participants': Cl.list([Cl.principal(wallet1)]) + })]) + ], + wallet3 // Not a member who can add expenses + ); + + expect(result).toBeErr(Cl.uint(200)); // ERR-UNAUTHORIZED + }); + + it("handles empty expense list", () => { + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-expenses", + [Cl.uint(1), Cl.list([])], + wallet1 + ); + + expect(result).toBeErr(Cl.uint(201)); // ERR-INVALID-AMOUNT (empty list) + }); + }); + + describe("Bulk Debt Settlement", () => { + beforeEach(() => { + // Create some expenses to generate debts + simnet.callPublicFn( + "circle-treasury", + "add-expense", + [ + Cl.uint(1), + Cl.stringAscii("Dinner"), + Cl.uint(300000000), + Cl.list([Cl.principal(wallet1), Cl.principal(wallet2), Cl.principal(wallet3)]) + ], + wallet1 + ); + + simnet.callPublicFn( + "circle-treasury", + "add-expense", + [ + Cl.uint(1), + Cl.stringAscii("Lunch"), + Cl.uint(200000000), + Cl.list([Cl.principal(wallet2), Cl.principal(wallet3)]) + ], + wallet2 + ); + }); + + it("settles multiple debts successfully", () => { + const settlements = [ + { creditor: wallet1 }, // wallet2 owes wallet1 from dinner + { creditor: wallet2 } // wallet3 owes wallet2 from lunch + ]; + + const { result } = simnet.callPublicFn( + "circle-treasury", + "settle-multiple-debts", + [ + Cl.uint(1), + Cl.list(settlements.map(s => Cl.tuple({ + 'creditor': Cl.principal(s.creditor) + }))) + ], + wallet2 // wallet2 settling debts + ); + + expect(result).toBeOk(Cl.list([ + Cl.uint(1), // First settlement ID + Cl.uint(2) // Second settlement ID + ])); + }); + + it("handles debts with no balance", () => { + const settlements = [ + { creditor: wallet1 }, // Valid debt + { creditor: wallet3 } // No debt between wallet2 and wallet3 + ]; + + const { result } = simnet.callPublicFn( + "circle-treasury", + "settle-multiple-debts", + [ + Cl.uint(1), + Cl.list(settlements.map(s => Cl.tuple({ + 'creditor': Cl.principal(s.creditor) + }))) + ], + wallet2 + ); + + expect(result).toBeErr(Cl.uint(205)); // ERR-NO-DEBT from second settlement + }); + + it("requires active membership", () => { + const { result } = simnet.callPublicFn( + "circle-treasury", + "settle-multiple-debts", + [ + Cl.uint(1), + Cl.list([Cl.tuple({ + 'creditor': Cl.principal(wallet1) + })]) + ], + wallet4 // Not a member + ); + + expect(result).toBeErr(Cl.uint(206)); // ERR-MEMBER-NOT-FOUND + }); + }); + + describe("Bulk Member Addition", () => { + it("adds multiple members successfully", () => { + const newMembers = [ + { member: wallet2, nickname: "Bob" }, + { member: wallet3, nickname: "Charlie" }, + { member: wallet4, nickname: "David" } + ]; + + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-members", + [ + Cl.uint(1), + Cl.list(newMembers.map(m => Cl.tuple({ + 'member': Cl.principal(m.member), + 'nickname': Cl.stringAscii(m.nickname) + }))) + ], + wallet1 // Creator + ); + + expect(result).toBeOk(Cl.list([ + Cl.bool(true), // First addition success + Cl.bool(true), // Second addition success + Cl.bool(true) // Third addition success + ])); + }); + + it("validates member limits", () => { + // Try to add more members than the limit allows + const manyMembers = Array.from({length: 48}, (_, i) => ({ + member: accounts.get(`wallet_${i + 5}`)!, // wallet_5 onwards + nickname: `User${i + 5}` + })); + + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-members", + [ + Cl.uint(1), + Cl.list(manyMembers.map(m => Cl.tuple({ + 'member': Cl.principal(m.member), + 'nickname': Cl.stringAscii(m.nickname) + }))) + ], + wallet1 + ); + + expect(result).toBeErr(Cl.uint(210)); // ERR-MAX-MEMBERS + }); + + it("requires creator authorization", () => { + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-members", + [ + Cl.uint(1), + Cl.list([Cl.tuple({ + 'member': Cl.principal(wallet4), + 'nickname': Cl.stringAscii("Hacker") + })]) + ], + wallet2 // Not creator + ); + + expect(result).toBeErr(Cl.uint(200)); // ERR-UNAUTHORIZED + }); + + it("handles duplicate members in bulk", () => { + // Add wallet2 first + simnet.callPublicFn( + "circle-treasury", + "add-member", + [Cl.uint(1), Cl.principal(wallet2), Cl.stringAscii("Bob")], + wallet1 + ); + + // Try to add wallet2 again in bulk + const { result } = simnet.callPublicFn( + "circle-treasury", + "add-multiple-members", + [ + Cl.uint(1), + Cl.list([Cl.tuple({ + 'member': Cl.principal(wallet2), + 'nickname': Cl.stringAscii("Bob2") + })]) + ], + wallet1 + ); + + expect(result).toBeErr(Cl.uint(209)); // ERR-MEMBER-EXISTS + }); + }); + + describe("Bulk Operation Integration", () => { + it("combines bulk operations in complex workflow", () => { + // 1. Bulk add members + simnet.callPublicFn( + "circle-treasury", + "add-multiple-members", + [ + Cl.uint(1), + Cl.list([ + Cl.tuple({ + 'member': Cl.principal(wallet2), + 'nickname': Cl.stringAscii("Bob") + }), + Cl.tuple({ + 'member': Cl.principal(wallet3), + 'nickname': Cl.stringAscii("Charlie") + }), + Cl.tuple({ + 'member': Cl.principal(wallet4), + 'nickname': Cl.stringAscii("David") + }) + ]) + ], + wallet1 + ); + + // 2. Bulk add expenses + simnet.callPublicFn( + "circle-treasury", + "add-multiple-expenses", + [ + Cl.uint(1), + Cl.list([ + Cl.tuple({ + 'description': Cl.stringAscii("Dinner"), + 'amount': Cl.uint(400000000), + 'participants': Cl.list([Cl.principal(wallet1), Cl.principal(wallet2), Cl.principal(wallet3), Cl.principal(wallet4)]) + }), + Cl.tuple({ + 'description': Cl.stringAscii("Drinks"), + 'amount': Cl.uint(100000000), + 'participants': Cl.list([Cl.principal(wallet2), Cl.principal(wallet3), Cl.principal(wallet4)]) + }) + ]) + ], + wallet1 + ); + + // 3. Bulk settle debts + simnet.callPublicFn( + "circle-treasury", + "settle-multiple-debts", + [ + Cl.uint(1), + Cl.list([ + Cl.tuple({'creditor': Cl.principal(wallet1)}), // wallet2 settles with wallet1 + Cl.tuple({'creditor': Cl.principal(wallet1)}) // wallet3 settles with wallet1 + ]) + ], + wallet2 + ); + + // Verify balances are updated + const balance1 = simnet.callReadOnlyFn( + "circle-treasury", + "get-balance", + [Cl.uint(1), Cl.principal(wallet2), Cl.principal(wallet1)], + wallet1 + ); + expect(balance1.result).toBeUint(0); // Debt settled + + const balance2 = simnet.callReadOnlyFn( + "circle-treasury", + "get-balance", + [Cl.uint(1), Cl.principal(wallet3), Cl.principal(wallet1)], + wallet1 + ); + expect(balance2.result).toBeUint(0); // Debt settled + }); + + it("handles paused circle for bulk operations", () => { + // Pause the circle + simnet.callPublicFn( + "circle-treasury", + "pause-circle", + [Cl.uint(1)], + wallet1 + ); + + // Try bulk operations + const expenseResult = simnet.callPublicFn( + "circle-treasury", + "add-multiple-expenses", + [ + Cl.uint(1), + Cl.list([Cl.tuple({ + 'description': Cl.stringAscii("Test"), + 'amount': Cl.uint(100000000), + 'participants': Cl.list([Cl.principal(wallet1)]) + })]) + ], + wallet1 + ); + + expect(expenseResult.result).toBeErr(Cl.uint(208)); // ERR-CIRCLE-PAUSED + + const settlementResult = simnet.callPublicFn( + "circle-treasury", + "settle-multiple-debts", + [ + Cl.uint(1), + Cl.list([Cl.tuple({'creditor': Cl.principal(wallet1)})]) + ], + wallet2 + ); + + expect(settlementResult.result).toBeErr(Cl.uint(208)); // ERR-CIRCLE-PAUSED + }); + }); + }); }); From 49056747eab672f18736a17d7616dd9598626ccb Mon Sep 17 00:00:00 2001 From: SmartFlow Developer Date: Tue, 20 Jan 2026 06:35:32 +0100 Subject: [PATCH 2/2] Add project structure improvements - Add MIT LICENSE file for open source compliance - Update .gitignore to allow .github templates while keeping other .md files ignored - Add GitHub issue templates (bug report and feature request) for CircleCare care circles - Add pull request template with circle member/admin/expense creator specific checklists These changes improve contributor experience and project maintainability for the care circles expense sharing ecosystem. --- .github/ISSUE_TEMPLATE/bug_report.md | 59 ++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 70 +++++++++++++++++ .github/pull_request_template.md | 96 +++++++++++++++++++++++ .gitignore | 2 + LICENSE | 21 +++++ 5 files changed, 248 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md create mode 100644 LICENSE diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..c351b6d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,59 @@ +--- +name: Bug Report +about: Report a bug or issue with CircleCare care circles platform +title: '[BUG] ' +labels: bug +assignees: '' + +--- + +## Bug Description +A clear and concise description of the bug. + +## Steps to Reproduce +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +## Expected Behavior +A clear and concise description of what you expected to happen. + +## Actual Behavior +What actually happened instead. + +## Environment +- **Browser/OS**: [e.g. Chrome 91.0.4472.124 on macOS] +- **Stacks Network**: [e.g. Mainnet, Testnet, Devnet] +- **Wallet**: [e.g. Hiro Wallet, Xverse, Leather] +- **Contract Version**: [e.g. v1.0.0] +- **User Role**: [Circle Member, Circle Admin, Expense Creator] + +## Affected Component +- [ ] Circle Management Contract +- [ ] Expense Tracking Contract +- [ ] Settlement Contract +- [ ] Frontend Application +- [ ] Testing Suite +- [ ] Documentation + +## Circle/Expense Details +- **Circle ID**: [if applicable] +- **Expense ID**: [if applicable] +- **Transaction Type**: [Create Circle, Add Expense, Settle Expense, Join Circle] +- **STX Amount**: [if applicable] +- **Participants**: [number of people involved] + +## Additional Context +Add any other context about the problem here, such as: +- Screenshots +- Error messages +- Transaction IDs +- Contract state information +- Circle configuration details + +## Priority +- [ ] Critical (blocks core care circle functionality) +- [ ] High (major expense tracking issue) +- [ ] Medium (minor issue affecting user experience) +- [ ] Low (cosmetic or enhancement) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..ef66a97 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,70 @@ +--- +name: Feature Request +about: Suggest a new feature or enhancement for CircleCare care circles platform +title: '[FEATURE] ' +labels: enhancement +assignees: '' + +--- + +## Feature Summary +A brief description of the feature you'd like to see implemented. + +## Problem Statement +What problem does this feature solve? What pain point does it address for circle members, admins, or expense creators? + +## Proposed Solution +Describe your proposed solution in detail. Include: +- How it should work from the perspective of each user type (circle member/admin/expense creator) +- Smart contract modifications needed +- Frontend changes required +- Integration points with existing contracts + +## Alternative Solutions +Describe any alternative solutions or features you've considered. + +## Technical Implementation +### Smart Contracts +- [ ] New contract functions needed +- [ ] Modifications to existing contracts +- [ ] New contract deployment required +- [ ] Integration with existing contracts + +### Frontend +- [ ] New UI components +- [ ] Modified user interfaces +- [ ] New pages/routes +- [ ] API integrations + +### Testing +- [ ] Unit tests for new functionality +- [ ] Integration tests +- [ ] End-to-end testing requirements + +## User Impact +- **Circle Members**: How does this affect expense sharing and settlement? +- **Circle Admins**: How does this impact circle management and member administration? +- **Expense Creators**: How does this change expense creation and tracking? + +## Care Circle Considerations +- **Expense Types**: How does this affect different types of shared expenses? +- **Settlement Logic**: Any changes to automatic or manual settlement processes? +- **Privacy**: Impact on expense visibility and member privacy? +- **Scalability**: Performance implications for large care circles? + +## Additional Context +Add any other context or screenshots about the feature request here. + +## Implementation Notes +Technical details that might be helpful: +- Contract functions needed +- Data structures required +- Security considerations +- Performance implications +- Backward compatibility requirements + +## Priority +- [ ] Critical (core care circle functionality) +- [ ] High (important community feature) +- [ ] Medium (nice to have enhancement) +- [ ] Low (future consideration) \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..aa40a1d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,96 @@ +## Description +Brief description of the changes made in this PR. + +## Type of Change +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update +- [ ] Code refactoring +- [ ] Testing improvements + +## Changes Made +### Smart Contracts +- [ ] Modified Clarity contracts in `contracts/` +- [ ] Added new contract functions +- [ ] Updated contract interfaces +- [ ] Changed contract logic +- [ ] Updated contract documentation + +### Frontend +- [ ] Updated Next.js/React components +- [ ] Modified styling/CSS/Tailwind +- [ ] Added new pages/routes +- [ ] Updated dependencies +- [ ] Modified user interfaces + +### Testing +- [ ] Added new unit tests +- [ ] Updated existing tests +- [ ] Added integration tests +- [ ] Updated test coverage +- [ ] Added contract tests + +### Documentation +- [ ] Updated README +- [ ] Added code comments +- [ ] Updated API documentation +- [ ] Added deployment guides +- [ ] Updated contract documentation + +## User Role Impact +### For Circle Members +- [ ] Expense sharing changes +- [ ] Settlement process modifications +- [ ] Circle participation updates +- [ ] UI/UX improvements + +### For Circle Admins +- [ ] Circle management changes +- [ ] Member administration modifications +- [ ] Settings and configuration updates +- [ ] UI/UX improvements + +### For Expense Creators +- [ ] Expense creation changes +- [ ] Expense tracking modifications +- [ ] Receipt handling updates +- [ ] UI/UX improvements + +## Care Circle Impact +- [ ] Expense type handling changes +- [ ] Settlement logic modifications +- [ ] Privacy and visibility updates +- [ ] Performance improvements + +## Testing +- [ ] Unit tests pass (`npm test`) +- [ ] Integration tests pass +- [ ] Contract tests pass +- [ ] Manual testing completed +- [ ] Cross-browser testing done (if applicable) + +## Security Considerations +- [ ] Reviewed for potential security vulnerabilities +- [ ] No sensitive data exposure +- [ ] Contract functions are access-controlled appropriately +- [ ] Expense data privacy maintained +- [ ] User funds protection verified + +## Deployment Checklist +- [ ] Contract deployment tested on testnet +- [ ] Frontend builds successfully +- [ ] Environment variables configured +- [ ] Database migrations (if applicable) +- [ ] Monitoring/logging configured +- [ ] STX wallet integration tested + +## Screenshots (if applicable) +Add screenshots to show visual changes, especially for frontend modifications. + +## Additional Notes +Any additional information or context about this PR, such as: +- Migration guides for breaking changes +- Performance benchmarks +- Integration testing results +- Known limitations or future improvements \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8836e10..0808021 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,6 @@ Thumbs.db # Documentation (keep local only) docs/*.md +!.github/ *.md +!.github/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..95600a8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Adekunle Bamz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file