nexum-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nexum_sdk-0.1.0/PKG-INFO +12 -0
- nexum_sdk-0.1.0/README.md +124 -0
- nexum_sdk-0.1.0/nexum_sdk/__init__.py +6 -0
- nexum_sdk-0.1.0/nexum_sdk/client.py +77 -0
- nexum_sdk-0.1.0/nexum_sdk/constants.py +8 -0
- nexum_sdk-0.1.0/nexum_sdk/types.py +43 -0
- nexum_sdk-0.1.0/nexum_sdk/utils.py +15 -0
- nexum_sdk-0.1.0/nexum_sdk.egg-info/PKG-INFO +12 -0
- nexum_sdk-0.1.0/nexum_sdk.egg-info/SOURCES.txt +12 -0
- nexum_sdk-0.1.0/nexum_sdk.egg-info/dependency_links.txt +1 -0
- nexum_sdk-0.1.0/nexum_sdk.egg-info/requires.txt +3 -0
- nexum_sdk-0.1.0/nexum_sdk.egg-info/top_level.txt +1 -0
- nexum_sdk-0.1.0/pyproject.toml +20 -0
- nexum_sdk-0.1.0/setup.cfg +4 -0
nexum_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexum-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for NEXUM Protocol on Solana
|
|
5
|
+
Author-email: NEXUM Protocol <protocolnexum@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://nexum-protocol.netlify.app
|
|
8
|
+
Project-URL: Repository, https://github.com/Nexumprotocol/nexum-sdk
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: base58>=2.1.1
|
|
12
|
+
Requires-Dist: solders>=0.21.0
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# nexum-sdk (Python)
|
|
2
|
+
|
|
3
|
+
Official Python SDK for [NEXUM Protocol](https://nexum-protocol.netlify.app) — a decentralized freelance marketplace built on Solana.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install nexum-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Python 3.10+
|
|
14
|
+
- Dependencies: `httpx`, `base58`, `solders`
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from nexum_sdk import NexumClient, Network
|
|
20
|
+
|
|
21
|
+
# Connect to devnet
|
|
22
|
+
client = NexumClient.devnet()
|
|
23
|
+
|
|
24
|
+
# Connect to mainnet
|
|
25
|
+
client = NexumClient.mainnet()
|
|
26
|
+
|
|
27
|
+
# Custom RPC
|
|
28
|
+
client = NexumClient(network=Network.DEVNET, rpc_url="https://your-custom-rpc.com")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Tasks (async)
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from nexum_sdk import NexumClient, TaskStatus
|
|
36
|
+
|
|
37
|
+
client = NexumClient.devnet()
|
|
38
|
+
|
|
39
|
+
async def main():
|
|
40
|
+
# Fetch all open tasks
|
|
41
|
+
tasks = await client.get_open_tasks()
|
|
42
|
+
for task in tasks:
|
|
43
|
+
print(task.title, task.reward_sol, "SOL")
|
|
44
|
+
print(task.skills_list) # ['Rust', 'Solana']
|
|
45
|
+
|
|
46
|
+
# Fetch a specific task
|
|
47
|
+
task = await client.get_task(1)
|
|
48
|
+
if task:
|
|
49
|
+
print(task.title)
|
|
50
|
+
print(task.status) # TaskStatus.OPEN
|
|
51
|
+
|
|
52
|
+
# Fetch all tasks
|
|
53
|
+
all_tasks = await client.get_all_tasks()
|
|
54
|
+
|
|
55
|
+
# Total value locked
|
|
56
|
+
tvl = await client.get_tvl()
|
|
57
|
+
print(f"TVL: {tvl:.2f} SOL")
|
|
58
|
+
|
|
59
|
+
asyncio.run(main())
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Tasks (sync helpers)
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
client = NexumClient.devnet()
|
|
66
|
+
|
|
67
|
+
# Synchronous wrappers — no async/await needed
|
|
68
|
+
tasks = client.get_all_tasks_sync()
|
|
69
|
+
tvl = client.get_tvl_sync()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Utilities
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from nexum_sdk import (
|
|
76
|
+
sol_to_lamports,
|
|
77
|
+
lamports_to_sol,
|
|
78
|
+
get_sbt_level,
|
|
79
|
+
get_sbt_label,
|
|
80
|
+
days_left,
|
|
81
|
+
short_address,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
sol_to_lamports(1.5) # 1500000000
|
|
85
|
+
lamports_to_sol(1_000_000_000) # 1.0
|
|
86
|
+
|
|
87
|
+
get_sbt_level(20) # 3
|
|
88
|
+
get_sbt_label(3) # "Expert"
|
|
89
|
+
|
|
90
|
+
days_left(deadline_unix) # 7
|
|
91
|
+
|
|
92
|
+
short_address("7yn8tuqH...NNzA") # "7yn8...NNzA"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Constants
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from nexum_sdk import (
|
|
99
|
+
NEXUM_PROGRAM_ID,
|
|
100
|
+
NEXUM_DEVNET_RPC,
|
|
101
|
+
PLATFORM_FEE_BPS,
|
|
102
|
+
SBT_LEVELS,
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Types
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from nexum_sdk import NexumTask, NexumProfile, NexumDispute, TaskStatus, Network, TaskFilter
|
|
110
|
+
|
|
111
|
+
# Filter tasks
|
|
112
|
+
filter_ = TaskFilter(status=TaskStatus.IN_PROGRESS)
|
|
113
|
+
tasks = await client.get_all_tasks(filter_)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Links
|
|
117
|
+
|
|
118
|
+
- [NEXUM Protocol](https://nexum-protocol.netlify.app)
|
|
119
|
+
- [GitHub](https://github.com/Nexumprotocol/nexum-sdk)
|
|
120
|
+
- [PyPI](https://pypi.org/project/nexum-sdk)
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from .client import NexumClient
|
|
2
|
+
from .types import NexumTask, NexumProfile, NexumDispute, TaskStatus, Network, TaskFilter
|
|
3
|
+
from .constants import NEXUM_PROGRAM_ID, NEXUM_DEVNET_RPC, PLATFORM_FEE_BPS, SBT_LEVELS
|
|
4
|
+
from .utils import sol_to_lamports, lamports_to_sol, get_sbt_level, get_sbt_label, days_left, short_address
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import struct, asyncio, base64
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
import httpx
|
|
4
|
+
from .constants import NEXUM_PROGRAM_ID, NEXUM_DEVNET_RPC, NEXUM_MAINNET_RPC
|
|
5
|
+
from .types import NexumTask, NexumProfile, NexumDispute, TaskStatus, Network, TaskFilter
|
|
6
|
+
|
|
7
|
+
class NexumClient:
|
|
8
|
+
def __init__(self, network=Network.DEVNET, rpc_url=None):
|
|
9
|
+
self.network = network
|
|
10
|
+
self.program_id = NEXUM_PROGRAM_ID
|
|
11
|
+
self.rpc_url = rpc_url or (NEXUM_DEVNET_RPC if network == Network.DEVNET else NEXUM_MAINNET_RPC)
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def devnet(cls, rpc_url=None): return cls(Network.DEVNET, rpc_url)
|
|
15
|
+
@classmethod
|
|
16
|
+
def mainnet(cls, rpc_url=None): return cls(Network.MAINNET, rpc_url)
|
|
17
|
+
|
|
18
|
+
async def _rpc(self, method, params):
|
|
19
|
+
async with httpx.AsyncClient(timeout=30) as c:
|
|
20
|
+
r = await c.post(self.rpc_url, json={"jsonrpc":"2.0","id":1,"method":method,"params":params})
|
|
21
|
+
return r.json().get("result")
|
|
22
|
+
|
|
23
|
+
async def get_open_tasks(self): return await self.get_all_tasks(TaskFilter(status=TaskStatus.OPEN))
|
|
24
|
+
|
|
25
|
+
async def get_all_tasks(self, filter_=None):
|
|
26
|
+
result = await self._rpc("getProgramAccounts",[self.program_id,{"encoding":"base64","filters":[{"dataSize":892}]}])
|
|
27
|
+
if not result: return []
|
|
28
|
+
tasks = []
|
|
29
|
+
for item in result:
|
|
30
|
+
try:
|
|
31
|
+
data = base64.b64decode(item["account"]["data"][0])
|
|
32
|
+
tasks.append(self._deserialize_task(data))
|
|
33
|
+
except: continue
|
|
34
|
+
if filter_ and filter_.status:
|
|
35
|
+
tasks = [t for t in tasks if t.status == filter_.status]
|
|
36
|
+
return tasks
|
|
37
|
+
|
|
38
|
+
async def get_task(self, task_id):
|
|
39
|
+
try:
|
|
40
|
+
from .utils import sol_to_lamports
|
|
41
|
+
from solders.pubkey import Pubkey
|
|
42
|
+
import struct
|
|
43
|
+
id_bytes = struct.pack('<Q', task_id)
|
|
44
|
+
pda, _ = Pubkey.find_program_address([b"task", id_bytes], Pubkey.from_string(self.program_id))
|
|
45
|
+
result = await self._rpc("getAccountInfo",[str(pda),{"encoding":"base64"}])
|
|
46
|
+
if not result or not result.get("value"): return None
|
|
47
|
+
data = base64.b64decode(result["value"]["data"][0])
|
|
48
|
+
return self._deserialize_task(data)
|
|
49
|
+
except: return None
|
|
50
|
+
|
|
51
|
+
async def get_tvl(self):
|
|
52
|
+
tasks = await self.get_all_tasks()
|
|
53
|
+
return sum(t.reward_sol for t in tasks if t.status in [TaskStatus.OPEN, TaskStatus.IN_PROGRESS])
|
|
54
|
+
|
|
55
|
+
def _deserialize_task(self, data):
|
|
56
|
+
o = 8
|
|
57
|
+
task_id = struct.unpack_from('<Q',data,o)[0]; o+=8
|
|
58
|
+
creator = self._pk(data,o); o+=32
|
|
59
|
+
tl=struct.unpack_from('<I',data,o)[0];o+=4; title=data[o:o+tl].decode();o+=tl
|
|
60
|
+
dl=struct.unpack_from('<I',data,o)[0];o+=4; desc=data[o:o+dl].decode();o+=dl
|
|
61
|
+
sl=struct.unpack_from('<I',data,o)[0];o+=4; skills=data[o:o+sl].decode();o+=sl
|
|
62
|
+
reward=struct.unpack_from('<Q',data,o)[0];o+=8
|
|
63
|
+
deadline=struct.unpack_from('<q',data,o)[0];o+=8
|
|
64
|
+
sm={0:TaskStatus.OPEN,1:TaskStatus.IN_PROGRESS,2:TaskStatus.COMPLETED,3:TaskStatus.DISPUTED,4:TaskStatus.CANCELLED}
|
|
65
|
+
status=sm.get(data[o],TaskStatus.OPEN);o+=1
|
|
66
|
+
hw=data[o]==1;o+=1
|
|
67
|
+
worker=self._pk(data,o) if hw else None
|
|
68
|
+
if hw: o+=32
|
|
69
|
+
bump=data[o];o+=1; eb=data[o]
|
|
70
|
+
return NexumTask(task_id,creator,title,desc,skills,reward,deadline,status,worker,bump,eb)
|
|
71
|
+
|
|
72
|
+
def _pk(self,data,o):
|
|
73
|
+
import base58
|
|
74
|
+
return base58.b58encode(data[o:o+32]).decode()
|
|
75
|
+
|
|
76
|
+
def get_all_tasks_sync(self, filter_=None): return asyncio.run(self.get_all_tasks(filter_))
|
|
77
|
+
def get_tvl_sync(self): return asyncio.run(self.get_tvl())
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
NEXUM_PROGRAM_ID = "7yn8tuqHbNFRojEgiWeSoJkYjYtmdh1w4dKA2bCgNNzA"
|
|
2
|
+
NEXUM_DEVNET_RPC = "https://api.devnet.solana.com"
|
|
3
|
+
NEXUM_MAINNET_RPC = "https://api.mainnet-beta.solana.com"
|
|
4
|
+
PLATFORM_FEE_BPS = 250
|
|
5
|
+
LAMPORTS_PER_SOL = 1_000_000_000
|
|
6
|
+
SBT_LEVELS = {0:"Newcomer",1:"Contributor",2:"Builder",3:"Expert",4:"Legend"}
|
|
7
|
+
SBT_THRESHOLDS = [0, 5, 15, 30, 50]
|
|
8
|
+
SEEDS = {"task":b"task","escrow":b"escrow","profile":b"profile","dispute":b"dispute"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
class TaskStatus(str, Enum):
|
|
6
|
+
OPEN = "open"
|
|
7
|
+
IN_PROGRESS = "inProgress"
|
|
8
|
+
COMPLETED = "completed"
|
|
9
|
+
DISPUTED = "disputed"
|
|
10
|
+
CANCELLED = "cancelled"
|
|
11
|
+
|
|
12
|
+
class Network(str, Enum):
|
|
13
|
+
DEVNET = "devnet"
|
|
14
|
+
MAINNET = "mainnet-beta"
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class NexumTask:
|
|
18
|
+
task_id: int; creator: str; title: str; description: str
|
|
19
|
+
required_skills: str; reward_lamports: int; deadline_unix: int
|
|
20
|
+
status: TaskStatus; worker: Optional[str]; bump: int; escrow_bump: int
|
|
21
|
+
@property
|
|
22
|
+
def reward_sol(self): return self.reward_lamports / 1_000_000_000
|
|
23
|
+
@property
|
|
24
|
+
def skills_list(self): return [s.strip() for s in self.required_skills.split(',')]
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class NexumProfile:
|
|
28
|
+
owner: str; username: str; skills: str; reputation: int
|
|
29
|
+
tasks_completed: int; tasks_created: int; sbt_level: int; bump: int
|
|
30
|
+
@property
|
|
31
|
+
def sbt_label(self):
|
|
32
|
+
from .constants import SBT_LEVELS
|
|
33
|
+
return SBT_LEVELS.get(self.sbt_level, "Unknown")
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class NexumDispute:
|
|
37
|
+
task_id: int; opened_by: str; reason: str; resolved: bool; bump: int
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class TaskFilter:
|
|
41
|
+
status: Optional[TaskStatus] = None
|
|
42
|
+
creator: Optional[str] = None
|
|
43
|
+
worker: Optional[str] = None
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import time, struct
|
|
2
|
+
from .constants import LAMPORTS_PER_SOL, SBT_LEVELS, SBT_THRESHOLDS, PLATFORM_FEE_BPS
|
|
3
|
+
|
|
4
|
+
def sol_to_lamports(sol): return int(sol * LAMPORTS_PER_SOL)
|
|
5
|
+
def lamports_to_sol(lamps): return lamps / LAMPORTS_PER_SOL
|
|
6
|
+
def get_sbt_level(tasks):
|
|
7
|
+
for i in range(len(SBT_THRESHOLDS)-1,-1,-1):
|
|
8
|
+
if tasks >= SBT_THRESHOLDS[i]: return i
|
|
9
|
+
return 0
|
|
10
|
+
def get_sbt_label(level): return SBT_LEVELS.get(level, "Unknown")
|
|
11
|
+
def days_left(deadline): return max(0, (deadline - int(time.time())) // 86400)
|
|
12
|
+
def short_address(pk): return f"{pk[:4]}...{pk[-4:]}"
|
|
13
|
+
def explorer_url(val, type_="address", cluster="devnet"):
|
|
14
|
+
cp = f"?cluster={cluster}" if cluster != "mainnet-beta" else ""
|
|
15
|
+
return f"https://explorer.solana.com/{type_}/{val}{cp}"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexum-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for NEXUM Protocol on Solana
|
|
5
|
+
Author-email: NEXUM Protocol <protocolnexum@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://nexum-protocol.netlify.app
|
|
8
|
+
Project-URL: Repository, https://github.com/Nexumprotocol/nexum-sdk
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: base58>=2.1.1
|
|
12
|
+
Requires-Dist: solders>=0.21.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
nexum_sdk/__init__.py
|
|
4
|
+
nexum_sdk/client.py
|
|
5
|
+
nexum_sdk/constants.py
|
|
6
|
+
nexum_sdk/types.py
|
|
7
|
+
nexum_sdk/utils.py
|
|
8
|
+
nexum_sdk.egg-info/PKG-INFO
|
|
9
|
+
nexum_sdk.egg-info/SOURCES.txt
|
|
10
|
+
nexum_sdk.egg-info/dependency_links.txt
|
|
11
|
+
nexum_sdk.egg-info/requires.txt
|
|
12
|
+
nexum_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nexum_sdk
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nexum-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for NEXUM Protocol on Solana"
|
|
9
|
+
license = {text = "MIT"}
|
|
10
|
+
authors = [{name = "NEXUM Protocol", email = "protocolnexum@gmail.com"}]
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = ["httpx>=0.27.0", "base58>=2.1.1", "solders>=0.21.0"]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://nexum-protocol.netlify.app"
|
|
16
|
+
Repository = "https://github.com/Nexumprotocol/nexum-sdk"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["."]
|
|
20
|
+
include = ["nexum_sdk*"]
|