From 19d3512b9d2d826b783ad82d61c97b21327efdd2 Mon Sep 17 00:00:00 2001 From: Dime Match Date: Wed, 6 Aug 2025 18:32:13 +0300 Subject: [PATCH] feat: Add SuperLiquidity module with GitHub reputation as microcollateral - Add github_reputation.fc for reputation scoring - Add superliquidity.fc main contract - Add comprehensive tests - Add module documentation --- contracts/superliquidity/README.md | 54 ++++++++ contracts/superliquidity/github_reputation.fc | 59 ++++++++ contracts/superliquidity/superliquidity.fc | 131 ++++++++++++++++++ .../tests/github_reputation_tests.fc | 54 ++++++++ 4 files changed, 298 insertions(+) create mode 100644 contracts/superliquidity/README.md create mode 100644 contracts/superliquidity/github_reputation.fc create mode 100644 contracts/superliquidity/superliquidity.fc create mode 100644 contracts/superliquidity/tests/github_reputation_tests.fc diff --git a/contracts/superliquidity/README.md b/contracts/superliquidity/README.md new file mode 100644 index 0000000..cad267c --- /dev/null +++ b/contracts/superliquidity/README.md @@ -0,0 +1,54 @@ +# SuperLiquidity Module for OpenLib.FunC + +This module provides implementation of SuperLiquidity concept using GitHub reputation as microcollateral. + +## Components + +### github_reputation.fc +Implements reputation scoring system for GitHub accounts based on: +- Repository stars (40% weight) +- Followers count (30% weight) +- Contributions (20% weight) +- Account age (10% weight) + +### superliquidity.fc +Main smart contract implementing: +- Transaction management +- State transitions +- Reputation verification +- Microcollateral handling + +## Usage + +```func +#include "contracts/superliquidity/github_reputation.fc"; + +;; Calculate reputation score +int score = calculate_reputation_score( + stars, followers, contributions, account_age_days +); + +;; Verify for collateral +int is_valid = verify_reputation_for_collateral( + stars, followers, contributions, account_age_days, + required_score +); +``` + +## Testing + +Run tests using: +```bash +func -SPA contracts/superliquidity/tests/github_reputation_tests.fc +``` + +## Security + +- Minimum requirements prevent spam accounts +- Weighted scoring ensures balanced evaluation +- Transaction states prevent double-spending +- Trusted oracle required for GitHub data + +## License + +This module is released under the GPL-3.0 License. \ No newline at end of file diff --git a/contracts/superliquidity/github_reputation.fc b/contracts/superliquidity/github_reputation.fc new file mode 100644 index 0000000..612ac4c --- /dev/null +++ b/contracts/superliquidity/github_reputation.fc @@ -0,0 +1,59 @@ +;; GitHub Reputation Calculator for SuperLiquidity +;; This module evaluates GitHub accounts for microcollateral + +#include "stdlib.fc"; + +const int MIN_ACCOUNT_AGE_DAYS = 180; ;; 6 months minimum +const int MIN_STARS = 10; +const int MIN_FOLLOWERS = 5; +const int MIN_CONTRIBUTIONS = 50; + +;; Reputation structure +;; stars:int, followers:int, contributions:int, account_age_days:int -> reputation:int +(int, int, int, int) load_reputation(slice s) inline { + return (s~load_uint(32), ;; stars + s~load_uint(32), ;; followers + s~load_uint(32), ;; contributions + s~load_uint(32)); ;; account_age_days +} + +;; Store reputation data +builder store_reputation(builder b, int stars, int followers, int contributions, int account_age_days) inline { + return b.store_uint(stars, 32) + .store_uint(followers, 32) + .store_uint(contributions, 32) + .store_uint(account_age_days, 32); +} + +;; Calculate reputation score (0-100) +int calculate_reputation_score(int stars, int followers, int contributions, int account_age_days) inline { + ;; Basic requirements check + if (account_age_days < MIN_ACCOUNT_AGE_DAYS) { + return 0; + } + if ((stars < MIN_STARS) | (followers < MIN_FOLLOWERS) | (contributions < MIN_CONTRIBUTIONS)) { + return 0; + } + + ;; Weighted score calculation + int star_score = min(stars * 40 / 100, 40); ;; 40% weight for stars + int follower_score = min(followers * 30 / 100, 30); ;; 30% weight for followers + int contribution_score = min(contributions * 20 / 100, 20); ;; 20% weight for contributions + int age_score = min(account_age_days * 10 / 365, 10); ;; 10% weight for account age + + return min(star_score + follower_score + contribution_score + age_score, 100); +} + +;; Verify if reputation meets minimum requirements for microcollateral +int verify_reputation_for_collateral(int stars, int followers, int contributions, int account_age_days, int required_score) inline { + int score = calculate_reputation_score(stars, followers, contributions, account_age_days); + return score >= required_score; +} + +;; Get reputation data message +cell create_reputation_data_message(int stars, int followers, int contributions, int account_age_days) inline { + return begin_cell() + .store_uint(0x52455055, 32) ;; "REPU" marker + .store_reputation(stars, followers, contributions, account_age_days) + .end_cell(); +} \ No newline at end of file diff --git a/contracts/superliquidity/superliquidity.fc b/contracts/superliquidity/superliquidity.fc new file mode 100644 index 0000000..434f39c --- /dev/null +++ b/contracts/superliquidity/superliquidity.fc @@ -0,0 +1,131 @@ +;; SuperLiquidity Smart Contract with GitHub Reputation +;; Implements microcollateral system using GitHub account metrics + +#include "stdlib.fc"; +#include "github_reputation.fc"; + +const int op::provide_reputation = 0x50524f56; ;; "PROV" +const int op::create_transaction = 0x43524554; ;; "CRET" +const int op::match_transaction = 0x4d415443; ;; "MATC" +const int op::liquidate = 0x4c495155; ;; "LIQU" + +;; Transaction States +const int state::matching = 0; +const int state::done = 1; +const int state::liquidated = 2; + +;; Storage TL-B: +;; storage#_ owner_address:MsgAddress +;; transactions:(HashmapE 64 Transaction) +;; min_reputation:uint32 +;; = Storage; + +;; Transaction TL-B: +;; transaction#_ id:uint64 +;; state:uint8 +;; owner:MsgAddress +;; reputation_score:uint32 +;; amount:Coins +;; = Transaction; + +(slice, (cell, int)) load_data() inline { + slice ds = get_data().begin_parse(); + return (ds~load_msg_addr(), (ds~load_dict(), ds~load_uint(32))); +} + +() save_data(slice owner_address, cell transactions, int min_reputation) impure inline { + set_data(begin_cell() + .store_slice(owner_address) + .store_dict(transactions) + .store_uint(min_reputation, 32) + .end_cell()); +} + +() recv_internal(int msg_value, cell in_msg_full, slice in_msg_body) impure { + if (in_msg_body.slice_empty?()) { + return (); + } + + slice cs = in_msg_full.begin_parse(); + int flags = cs~load_uint(4); + if (flags & 1) { + return (); + } + slice sender_address = cs~load_msg_addr(); + + (slice owner_address, (cell transactions, int min_reputation)) = load_data(); + + int op = in_msg_body~load_uint(32); + + if (op == op::provide_reputation) { + ;; Process GitHub reputation data + (int stars, int followers, int contributions, int account_age_days) = load_reputation(in_msg_body); + int score = calculate_reputation_score(stars, followers, contributions, account_age_days); + + if (score >= min_reputation) { + ;; Store reputation data for the sender + cell reputation_data = create_reputation_data_message(stars, followers, contributions, account_age_days); + transactions~udict_set_ref(256, slice_hash(sender_address), reputation_data); + } + return save_data(owner_address, transactions, min_reputation); + } + + if (op == op::create_transaction) { + ;; Create new transaction with reputation check + int amount = in_msg_body~load_coins(); + int tx_id = now(); + + ;; Check if sender has valid reputation + (cell reputation_data, int found) = transactions~udict_get_ref?(256, slice_hash(sender_address)); + throw_unless(450, found); + + builder tx = begin_cell() + .store_uint(tx_id, 64) + .store_uint(state::matching, 8) + .store_slice(sender_address) + .store_coins(amount); + + transactions~udict_set_builder(64, tx_id, tx); + return save_data(owner_address, transactions, min_reputation); + } + + if (op == op::match_transaction) { + ;; Match existing transaction + int tx_id = in_msg_body~load_uint(64); + (slice tx_data, int found) = transactions~udict_get?(64, tx_id); + throw_unless(451, found); + + int current_state = tx_data~load_uint(8); + throw_unless(452, current_state == state::matching); + + ;; Update transaction state + builder new_tx = begin_cell() + .store_uint(tx_id, 64) + .store_uint(state::done, 8) + .store_slice(tx_data); + + transactions~udict_set_builder(64, tx_id, new_tx); + return save_data(owner_address, transactions, min_reputation); + } + + if (op == op::liquidate) { + ;; Liquidate transaction + int tx_id = in_msg_body~load_uint(64); + (slice tx_data, int found) = transactions~udict_get?(64, tx_id); + throw_unless(451, found); + + int current_state = tx_data~load_uint(8); + throw_unless(453, current_state != state::done); + + ;; Update transaction state to liquidated + builder new_tx = begin_cell() + .store_uint(tx_id, 64) + .store_uint(state::liquidated, 8) + .store_slice(tx_data); + + transactions~udict_set_builder(64, tx_id, new_tx); + return save_data(owner_address, transactions, min_reputation); + } + + throw(0xffff); +} \ No newline at end of file diff --git a/contracts/superliquidity/tests/github_reputation_tests.fc b/contracts/superliquidity/tests/github_reputation_tests.fc new file mode 100644 index 0000000..792b28a --- /dev/null +++ b/contracts/superliquidity/tests/github_reputation_tests.fc @@ -0,0 +1,54 @@ +;; Tests for GitHub reputation calculation module + +#include "../github_reputation.fc"; + +int __test_basic_reputation_calculation() { + int stars = 100; + int followers = 50; + int contributions = 200; + int account_age_days = 365; ;; 1 year + + int score = calculate_reputation_score(stars, followers, contributions, account_age_days); + ;; Expected: ~85 (40 for stars + 30 for followers + 10 for age + 5 for contributions) + return score > 80 & score <= 100; +} + +int __test_minimum_requirements() { + ;; Test with values below minimum requirements + int score1 = calculate_reputation_score(5, 3, 20, 100); ;; Should be 0 + int score2 = calculate_reputation_score(100, 100, 100, 100); ;; Should be 0 (too young account) + return (score1 == 0) & (score2 == 0); +} + +int __test_reputation_data_serialization() { + int stars = 100; + int followers = 50; + int contributions = 200; + int account_age_days = 365; + + cell msg = create_reputation_data_message(stars, followers, contributions, account_age_days); + slice s = msg.begin_parse(); + + ;; Skip marker + s~load_uint(32); + + ;; Load and verify data + (int loaded_stars, int loaded_followers, int loaded_contributions, int loaded_age) = load_reputation(s); + + return (loaded_stars == stars) & + (loaded_followers == followers) & + (loaded_contributions == contributions) & + (loaded_age == account_age_days); +} + +int __test_collateral_verification() { + int required_score = 70; + + ;; Test valid collateral + int valid = verify_reputation_for_collateral(100, 50, 200, 365, required_score); + + ;; Test invalid collateral + int invalid = verify_reputation_for_collateral(10, 5, 50, 180, required_score); + + return valid & ~invalid; +} \ No newline at end of file