Linkora-socials is an early-stage open source SocialFi project built on Stellar with Soroban smart contracts. The current repository is focused on the protocol foundation: a Rust contract workspace that models creator profiles, follow relationships, social posts, token tipping, and community pools.
This project is intended to serve as a starting point for contributors exploring social and creator-economy primitives on Stellar.
Linkora-socials is in the foundation stage.
- The repository currently contains the Soroban contracts workspace.
- Core social and token interaction primitives are implemented and covered by unit tests.
- Frontend, indexing, and backend services are not yet included in this repository.
If you are submitting this project to a Stellar open source contribution platform, this repository should be presented as a protocol prototype rather than a complete end-user application.
The main contract in packages/contracts/contracts/linkora-contracts currently supports:
- Profile registration and updates
- Follow relationships between accounts
- On-chain post creation
- Tipping posts with SEP-41 compatible tokens
- Community pool deposits and withdrawals
These primitives provide a minimal base for experimenting with social-financial interactions on Soroban.
.
├── package.json
├── pnpm-workspace.yaml
├── turbo.json
└── packages
└── contracts
├── Cargo.toml
├── package.json
└── contracts
└── linkora-contracts
├── Cargo.toml
├── Makefile
└── src
├── lib.rs
└── test.rs
- Stellar Soroban smart contracts
- Rust
soroban-sdk- Cargo workspace
pnpmworkspaces- Turborepo for task orchestration
The primary contract is LinkoraContract.
Profile: stores a user address, username, and creator token addressPost: stores post id, author, content, total tips, timestamp, and like countPool: stores a pool token address and tracked balance
| Function | Purpose | Required signer | Inputs | Returns |
|---|---|---|---|---|
initialize(admin, treasury, fee_bps) |
One-time contract setup. Panics if called more than once. | admin |
admin: Address — contract administratortreasury: Address — fee recipientfee_bps: u32 — protocol fee in basis points (0–10 000) |
() |
set_profile(user, username, creator_token) |
Register or update a creator profile. | user |
user: Address — account being registeredusername: String — display name (3–32 alphanumeric or _ characters)creator_token: Address — SEP-41 token the creator has deployed (pass own address if none) |
() |
get_profile(user) |
Fetch a profile by address. | None | user: Address |
Option<Profile> |
get_profile_count() |
Return the total number of registered profiles. | None | None | u64 |
follow(follower, followee) |
Record a follow relationship. Duplicate follows are ignored. Panics if followee has blocked follower. |
follower |
follower: Address — account initiating the followfollowee: Address — account being followed |
() |
unfollow(follower, followee) |
Remove a follow relationship. No-op if the relationship does not exist. | follower |
follower: Address — account removing the followfollowee: Address — account being unfollowed |
() |
get_following(user) |
Return all accounts followed by a user. | None | user: Address |
Vec<Address> |
get_followers(user) |
Return all accounts that follow a user. | None | user: Address |
Vec<Address> |
block_user(blocker, blocked) |
Add an account to the caller's block list, preventing them from following. | blocker |
blocker: Address — account initiating the blockblocked: Address — account being blocked |
() |
unblock_user(blocker, blocked) |
Remove an account from the caller's block list. | blocker |
blocker: Address — account removing the blockblocked: Address — account being unblocked |
() |
is_blocked(blocker, blocked) |
Check whether blocker has blocked blocked. |
None | blocker: Addressblocked: Address |
bool |
create_post(author, content) |
Publish a new on-chain post. Post IDs are assigned sequentially starting at 1. | author |
author: Address — post creatorcontent: String — post body (1–280 characters) |
u64 — new post ID |
get_post_count() |
Return the total number of posts created so far. Returns 0 when no posts exist. |
None | None | u64 |
get_post(id) |
Fetch a post by ID. | None | id: u64 |
Option<Post> |
delete_post(author, post_id) |
Delete a post. Only the original author may delete their own post. | author |
author: Address — post ownerpost_id: u64 — ID of the post to delete |
() |
like_post(user, post_id) |
Like a post. Duplicate likes from the same user are ignored. | user |
user: Address — account liking the postpost_id: u64 — target post |
() |
get_like_count(post_id) |
Return the number of likes on a post. | None | post_id: u64 |
u64 |
has_liked(user, post_id) |
Check whether a user has liked a specific post. | None | user: Addresspost_id: u64 |
bool |
tip(tipper, post_id, token, amount) |
Transfer SEP-41 tokens to a post's author, applying the protocol fee, and increment the post's tip_total. |
tipper |
tipper: Address — senderpost_id: u64 — target posttoken: Address — SEP-41 token contractamount: i128 — token units to transfer (must be > 0) |
() |
create_pool(admin, pool_id, token, initial_admins, threshold) |
Create a named community pool with an M-of-N admin set. Requires contract admin auth. | contract admin |
admin: Address — caller (must be contract admin)pool_id: Symbol — unique pool identifiertoken: Address — SEP-41 token for the poolinitial_admins: Vec<Address> — admin setthreshold: u32 — minimum signatures required to withdraw (must be > 0 and ≤ initial_admins.len()) |
() |
pool_deposit(depositor, pool_id, token, amount) |
Deposit tokens into a named community pool. amount must be greater than zero. |
depositor |
depositor: Address — token senderpool_id: Symbol — pool identifiertoken: Address — SEP-41 token contract (must match pool token)amount: i128 — token units to deposit (must be > 0) |
() |
pool_withdraw(signers, pool_id, amount, recipient) |
Withdraw tokens from a community pool. Requires at least threshold valid admin signatures from the pool's admin set. |
each address in signers |
signers: Vec<Address> — admin addresses authorising the withdrawalpool_id: Symbol — pool identifieramount: i128 — token units to withdraw (must be > 0 and ≤ pool balance)recipient: Address — token receiver |
() |
get_pool(pool_id) |
Fetch the current state of a pool. | None | pool_id: Symbol |
Option<Pool> |
set_fee(fee_bps) |
Update the protocol fee. Only callable by the contract admin. | contract admin |
fee_bps: u32 — new fee in basis points (0–10 000) |
() |
set_treasury(treasury) |
Update the treasury address that receives protocol fees. Only callable by the contract admin. | contract admin |
treasury: Address — new fee recipient |
() |
get_fee_bps() |
Return the current protocol fee in basis points. | None | None | u32 |
get_treasury() |
Return the current treasury address. | None | None | Option<Address> |
upgrade(new_wasm_hash) |
Upgrade the contract WASM. Only callable by the contract admin. | contract admin |
new_wasm_hash: BytesN<32> — hash of the new WASM blob |
() |
Linkora-socials uses Soroban's state storage to manage its data. Below is a summary of the storage keys and namespaces used by the contract.
- Instance Storage: Used for contract-wide configuration and small, frequently updated counters (e.g., admin address, post counter).
- Persistent Storage: Used for all user-generated data like profiles, posts, and social relationships. This data is subject to TTL extensions to remain on-chain.
| Key | Format | Namespace | Purpose |
|---|---|---|---|
PROFILES |
(Symbol("PROFILES"), Address) |
Persistent | Stores user Profile data. |
PROF_CT |
Symbol("PROF_CT") |
Instance | Tracks the total number of registered profiles. |
FOLLOWS |
(Symbol("FOLLOWS"), Address) |
Persistent | Stores a Vec<Address> of accounts that the given address follows. |
FOLLOWRS |
(Symbol("FOLLOWRS"), Address) |
Persistent | Stores a Vec<Address> of accounts following the given address. |
BLOCKS |
(Symbol("BLOCKS"), Address) |
Persistent | Stores a Map<Address, ()> of accounts blocked by the given address. |
POSTS |
(Symbol("POSTS"), u64) |
Persistent | Stores individual Post objects by their incremental ID. |
POST_CT |
Symbol("POST_CT") |
Instance | Tracks the total number of posts created (used for ID generation). |
POOLS |
(Symbol("POOLS"), Symbol) |
Persistent | Stores Pool data for named community pools. |
LIKES |
(Symbol("LIKES"), u64, Address) |
Persistent | Records whether a specific user has liked a specific post. |
ADMIN |
Symbol("ADMIN") |
Instance | Stores the contract administrator's address. |
TREASURY |
Symbol("TREASURY") |
Instance | Stores the treasury address that receives protocol fees. |
FEE_BPS |
Symbol("FEE_BPS") |
Instance | Stores the protocol fee in basis points (0–10 000). |
INIT |
Symbol("INIT") |
Instance | Boolean flag indicating if the contract has been initialized. |
Note
This storage layout is designed for the prototype phase and has not been optimized for large-scale data or minimal footprint.
Install the following before working on the project:
- Node.js 18+ recommended
pnpm9+- Rust toolchain
- Stellar CLI with Soroban support
Example installation for the Stellar CLI:
cargo install --locked stellar-cliIf your environment uses the older package naming, soroban-cli may also be valid depending on the installed tooling version.
pnpm installFrom the repository root:
pnpm build:contractsOr from the contracts package:
cd packages/contracts
pnpm buildFrom the repository root:
pnpm --filter contracts testOr:
cd packages/contracts
cargo testAt the repository root:
pnpm devpnpm buildpnpm build:contractspnpm lintpnpm testpnpm format
Inside packages/contracts:
pnpm buildpnpm testpnpm devpnpm format
The contract test suite currently covers:
- profile creation
- follow graph updates
- post creation
- tipping flow with token transfers
- community pool deposit and withdrawal flow
Tests are located in packages/contracts/contracts/linkora-contracts/src/test.rs.
Sandbox-backed integration tests with real transaction signing are available under tests/integration.
Run them from repository root:
pnpm test:integrationSee tests/README.md for setup details and CI guidance.
Contributions are welcome, especially in these areas:
- contract hardening and security review
- event design and indexing strategy
- access control and governance for pool withdrawals
- better storage layout and scalability improvements
- frontend and API integration work
- documentation and developer tooling
When contributing:
- keep changes focused and reviewable
- prefer small pull requests
- add or update tests for behavior changes
- document any new contract method or breaking interface change
Please review SECURITY.md for vulnerability disclosure guidance and scope.
pnpmcommand not found: Install pnpm globally usingnpm install -g pnpm. Linkora uses pnpm workspaces for managing multiple packages.stellarcommand not found: Install the Stellar CLI withcargo install --locked stellar-cli. Ensure~/.cargo/binis in your system PATH.cargo testfailing: Make sure you are running it from insidepackages/contracts. If you are at the repository root, usepnpm testinstead.- Outdated dependencies: Always run
pnpm installfrom the root directory after pulling new changes to ensure yournode_modulesand Turborepo cache are synchronized. - Rust build errors: Ensure the Wasm target is installed:
rustup target add wasm32-unknown-unknown.
| Task | Root Directory | packages/contracts |
|---|---|---|
| Install dependencies | pnpm install |
- |
| Build Contracts | pnpm build:contracts |
pnpm build |
| Run Tests | pnpm test |
cargo test |
This repository is a prototype and should not be treated as production-ready infrastructure yet.
- Pool withdrawal uses M-of-N admin authorization; more advanced governance may be needed for production.
- Contract storage layout has not been optimized for scale.
- No deployment scripts, frontend client, or backend service are included yet.
- Security review and audit work remain outstanding.
Planned next steps include:
- Strengthen contract authorization and safety checks
- Add events and indexer-friendly contract patterns
- Introduce deployment and environment tooling
- Build application-facing SDK or client helpers
- Add web and backend components around the contract layer
Linkora-socials explores how Stellar can support more than payments by combining social interaction with programmable asset flows. The goal is to make creator economies, community incentives, and lightweight SocialFi mechanics easier to build on Soroban.
This repository is licensed under the MIT License.