foundry-client 1.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 FoundryNet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.4
2
+ Name: foundry-client
3
+ Version: 1.0.0
4
+ Summary: Python client for FoundryNet - Universal DePIN Protocol for Work Settlement
5
+ Home-page: https://github.com/foundrynet/foundry_net_MINT
6
+ Author: FoundryNet
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: PyNaCl>=1.5.0
19
+ Requires-Dist: base58>=2.1.1
20
+ Requires-Dist: requests>=2.31.0
21
+ Dynamic: author
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license-file
27
+ Dynamic: requires-dist
28
+ Dynamic: requires-python
29
+ Dynamic: summary
30
+
31
+ FoundryNet
32
+ Earn MINT tokens for verified manufacturing work.
33
+ FoundryNet rewards 3D printers, CNC machines, robots, and production equipment with cryptocurrency for completing jobs. No staking, no capital required—just connect your machine and start earning.
34
+
35
+ Quick Start
36
+ Installation
37
+ bashnpm install tweetnacl bs58 uuid
38
+ Download foundry-client.js from the GitHub repository.
39
+ Basic Usage
40
+ javascriptimport { FoundryClient } from './foundry-client.js';
41
+
42
+ // Initialize client
43
+ const client = new FoundryClient({
44
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts',
45
+ debug: true
46
+ });
47
+
48
+ // Register machine (first time only - saves credentials locally)
49
+ await client.init({
50
+ type: '3d-printer',
51
+ model: 'Ender 3'
52
+ });
53
+
54
+ // Start a job
55
+ const jobHash = client.generateJobHash('benchy.gcode');
56
+ await client.submitJob(jobHash, {
57
+ job_type: 'print',
58
+ filename: 'benchy.gcode'
59
+ });
60
+
61
+ // Complete job and earn MINT
62
+ const result = await client.completeJob(
63
+ jobHash,
64
+ 'YOUR_SOLANA_WALLET_ADDRESS'
65
+ );
66
+
67
+ console.log(`Earned ${result.reward} MINT!`);
68
+ console.log(`View transaction: ${result.solscan}`);
69
+ That's it. Your machine is now earning.
70
+
71
+ How It Works
72
+ 1. Register Your Machine
73
+ Connect any machine that can make HTTP requests and sign messages (ed25519).
74
+ Supported:
75
+
76
+ 3D Printers (OctoPrint, Klipper, Marlin)
77
+ CNC Mills/Routers (GRBL, LinuxCNC)
78
+ Laser Cutters
79
+ Pick-and-Place Robots
80
+ Custom DIY machines
81
+
82
+ 2. Submit Jobs
83
+ When your machine starts productive work:
84
+ javascriptconst jobHash = client.generateJobHash('my_part.gcode');
85
+ await client.submitJob(jobHash, {
86
+ job_type: 'print',
87
+ filename: 'benchy.gcode'
88
+ });
89
+ 3. Earn MINT
90
+ When work completes successfully:
91
+ javascriptawait client.completeJob(jobHash, yourSolanaWallet);
92
+ // Instant payout: 3 MINT per job
93
+ Current Rewards:
94
+
95
+ 3 MINT per completed job
96
+ Maximum 400 MINT per machine per 24 hours
97
+ Jobs must run minimum 60 seconds
98
+
99
+
100
+ Integration Examples
101
+ OctoPrint Plugin
102
+ pythonfrom foundrynet import FoundryClient
103
+
104
+ # In plugin initialization
105
+ client = FoundryClient(api_url='https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts')
106
+ client.init(metadata={'type': 'printer', 'model': 'Prusa MK3'})
107
+
108
+ # On print start
109
+ def on_print_started(filename):
110
+ job_hash = client.generate_job_hash(filename)
111
+ client.submit_job(job_hash, {'filename': filename})
112
+
113
+ # On print complete
114
+ def on_print_done(job_hash):
115
+ client.complete_job(job_hash, user_wallet)
116
+ Klipper/Moonraker
117
+ python# Listen to Moonraker webhooks
118
+ def on_print_started(data):
119
+ job_hash = client.generate_job_hash(data['filename'])
120
+ client.submit_job(job_hash, {'filename': data['filename']})
121
+
122
+ def on_print_complete(data):
123
+ client.complete_job(current_job_hash, user_wallet)
124
+ GRBL/CNC
125
+ javascript// On program start
126
+ const jobHash = client.generateJobHash(programName);
127
+ await client.submitJob(jobHash, {
128
+ job_type: 'cnc',
129
+ program: programName
130
+ });
131
+
132
+ // On program complete (M2/M30)
133
+ await client.completeJob(jobHash, wallet);
134
+ Custom/DIY
135
+ javascript// Your machine, your rules
136
+ const client = new FoundryClient({
137
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts'
138
+ });
139
+
140
+ await client.init({
141
+ type: 'custom',
142
+ model: 'Pick and Place Robot'
143
+ });
144
+
145
+ // When task starts
146
+ const jobHash = client.generateJobHash(taskId);
147
+ await client.submitJob(jobHash, {
148
+ job_type: 'robot',
149
+ task_id: taskId
150
+ });
151
+
152
+ // When task completes
153
+ await client.completeJob(jobHash, wallet);
154
+
155
+ API Reference
156
+ FoundryClient(config)
157
+ javascriptconst client = new FoundryClient({
158
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts',
159
+ retryAttempts: 3, // Network retry count
160
+ retryDelay: 2000, // ms between retries
161
+ debug: false // Enable verbose logging
162
+ });
163
+ client.init(metadata)
164
+ Initialize machine. Loads existing credentials or generates new ones.
165
+ javascriptawait client.init({
166
+ type: 'printer', // printer|cnc|robot|laser|custom
167
+ model: 'Ender 3 V2',
168
+ firmware: 'Klipper'
169
+ });
170
+ // Saves credentials to .foundry_credentials.json
171
+ Returns:
172
+ javascript{
173
+ existing: true, // Whether credentials already existed
174
+ machineUuid: "abc-123", // Your machine ID
175
+ identity: {...} // New credentials (if first time)
176
+ }
177
+ client.submitJob(jobHash, payload)
178
+ Register job start.
179
+ javascriptawait client.submitJob('job_unique_hash', {
180
+ job_type: 'print',
181
+ filename: 'part.gcode',
182
+ estimated_time: 3600
183
+ });
184
+ Returns:
185
+ javascript{
186
+ success: true,
187
+ job_hash: "job_unique_hash"
188
+ }
189
+ client.completeJob(jobHash, recipientWallet)
190
+ Complete job and earn MINT.
191
+ javascriptconst result = await client.completeJob(
192
+ 'job_unique_hash',
193
+ 'YOUR_SOLANA_WALLET_ADDRESS'
194
+ );
195
+ Returns:
196
+ javascript{
197
+ success: true,
198
+ reward: 3,
199
+ tx_signature: "5x7y...",
200
+ solscan: "https://solscan.io/tx/..."
201
+ }
202
+ client.generateJobHash(filename, additionalData)
203
+ Generate deterministic job hash.
204
+ javascriptconst jobHash = client.generateJobHash('benchy.gcode');
205
+ // Returns: "job_abc123_1234567890"
206
+
207
+ Rules & Limits
208
+
209
+ 3 MINT per completed job
210
+ 400 MINT maximum per machine per 24 hours
211
+ Jobs must run minimum 60 seconds
212
+ Each job hash must be unique (no replays)
213
+ Signature timestamp must be within 5 minutes
214
+
215
+ What Counts as a Job?
216
+ Any discrete unit of productive work:
217
+
218
+ One 3D print from start to finish
219
+ One CNC machining operation
220
+ One robot task cycle
221
+ One laser cut/engrave job
222
+
223
+ What Doesn't Count?
224
+
225
+ Machine idle time
226
+ Calibration/homing
227
+ Test runs
228
+ Failed or cancelled jobs
229
+
230
+
231
+ Security
232
+ Proof of Productivity (PoP)
233
+ Every job completion requires:
234
+
235
+ Unique job hash (prevents replay attacks)
236
+ Ed25519 signature from registered machine keypair
237
+ Recent timestamp (within 5 minutes)
238
+ Minimum duration (60 seconds)
239
+ Rate limiting (400 MINT/24hrs per machine)
240
+
241
+ Your Machine Keys
242
+
243
+ Generated locally on first init()
244
+ Stored in .foundry_credentials.json
245
+ Never leave your machine
246
+ Back them up securely
247
+
248
+ Anti-Fraud
249
+
250
+ Job hash uniqueness enforced
251
+ Signature verification prevents impersonation
252
+ Rate limits prevent spam
253
+ Duration checks prevent instant gaming
254
+
255
+
256
+ Troubleshooting
257
+ "Job completion failed"
258
+
259
+ Check your machine credentials are loaded (client.init())
260
+ Verify job was submitted before completing
261
+ Ensure wallet address is valid Solana address
262
+
263
+ "Rate limit exceeded"
264
+
265
+ You've earned 400 MINT in the last 24 hours
266
+ Wait for the rolling window to reset
267
+ Check dashboard for current limits
268
+
269
+ "Signature verification failed"
270
+
271
+ Machine credentials may be corrupted
272
+ Delete .foundry_credentials.json and re-run init()
273
+ Ensure system time is accurate (for timestamps)
274
+
275
+ "Job duration too short"
276
+
277
+ Jobs must run minimum 60 seconds
278
+ Ensure you're measuring actual work time
279
+ Don't complete jobs immediately after starting
280
+
281
+ "Treasury depleted"
282
+
283
+ System maintenance in progress
284
+ Jobs are queued and will process when treasury refills
285
+ Check status updates
286
+
287
+
288
+ Getting a Wallet?
289
+ New to crypto?
290
+ Here's how to receive MINT:
291
+
292
+ Install Phantom Wallet (browser extension)
293
+ Create new wallet
294
+ Copy your Solana address
295
+ Use that address in completeJob()
296
+
297
+ MINT tokens appear instantly in your wallet after job completion.
298
+
299
+ Vision
300
+ FoundryNet is building payment infrastructure for autonomous industry.
301
+ We're starting simple: machines prove work happened, get paid instantly. The protocol handles identity, verification, and settlement. Everything else such as quality systems, marketplaces, reputation—gets built on top.
302
+ This is infrastructure for when AI agents coordinate supply chains and manufacturing capacity becomes programmable.
303
+
304
+ Tokenomics
305
+ Token: MINT (Solana SPL token)
306
+ Contract: 84hLMyG9cnC3Pd9LrWn4NtKMEhQHh4AvxbkcDMQtgem
307
+
308
+
309
+ Initial treasury funded by protocol creator
310
+ Rewards distributed for completed jobs
311
+ LP available on Raydium (MINT/SOL)
@@ -0,0 +1,281 @@
1
+ FoundryNet
2
+ Earn MINT tokens for verified manufacturing work.
3
+ FoundryNet rewards 3D printers, CNC machines, robots, and production equipment with cryptocurrency for completing jobs. No staking, no capital required—just connect your machine and start earning.
4
+
5
+ Quick Start
6
+ Installation
7
+ bashnpm install tweetnacl bs58 uuid
8
+ Download foundry-client.js from the GitHub repository.
9
+ Basic Usage
10
+ javascriptimport { FoundryClient } from './foundry-client.js';
11
+
12
+ // Initialize client
13
+ const client = new FoundryClient({
14
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts',
15
+ debug: true
16
+ });
17
+
18
+ // Register machine (first time only - saves credentials locally)
19
+ await client.init({
20
+ type: '3d-printer',
21
+ model: 'Ender 3'
22
+ });
23
+
24
+ // Start a job
25
+ const jobHash = client.generateJobHash('benchy.gcode');
26
+ await client.submitJob(jobHash, {
27
+ job_type: 'print',
28
+ filename: 'benchy.gcode'
29
+ });
30
+
31
+ // Complete job and earn MINT
32
+ const result = await client.completeJob(
33
+ jobHash,
34
+ 'YOUR_SOLANA_WALLET_ADDRESS'
35
+ );
36
+
37
+ console.log(`Earned ${result.reward} MINT!`);
38
+ console.log(`View transaction: ${result.solscan}`);
39
+ That's it. Your machine is now earning.
40
+
41
+ How It Works
42
+ 1. Register Your Machine
43
+ Connect any machine that can make HTTP requests and sign messages (ed25519).
44
+ Supported:
45
+
46
+ 3D Printers (OctoPrint, Klipper, Marlin)
47
+ CNC Mills/Routers (GRBL, LinuxCNC)
48
+ Laser Cutters
49
+ Pick-and-Place Robots
50
+ Custom DIY machines
51
+
52
+ 2. Submit Jobs
53
+ When your machine starts productive work:
54
+ javascriptconst jobHash = client.generateJobHash('my_part.gcode');
55
+ await client.submitJob(jobHash, {
56
+ job_type: 'print',
57
+ filename: 'benchy.gcode'
58
+ });
59
+ 3. Earn MINT
60
+ When work completes successfully:
61
+ javascriptawait client.completeJob(jobHash, yourSolanaWallet);
62
+ // Instant payout: 3 MINT per job
63
+ Current Rewards:
64
+
65
+ 3 MINT per completed job
66
+ Maximum 400 MINT per machine per 24 hours
67
+ Jobs must run minimum 60 seconds
68
+
69
+
70
+ Integration Examples
71
+ OctoPrint Plugin
72
+ pythonfrom foundrynet import FoundryClient
73
+
74
+ # In plugin initialization
75
+ client = FoundryClient(api_url='https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts')
76
+ client.init(metadata={'type': 'printer', 'model': 'Prusa MK3'})
77
+
78
+ # On print start
79
+ def on_print_started(filename):
80
+ job_hash = client.generate_job_hash(filename)
81
+ client.submit_job(job_hash, {'filename': filename})
82
+
83
+ # On print complete
84
+ def on_print_done(job_hash):
85
+ client.complete_job(job_hash, user_wallet)
86
+ Klipper/Moonraker
87
+ python# Listen to Moonraker webhooks
88
+ def on_print_started(data):
89
+ job_hash = client.generate_job_hash(data['filename'])
90
+ client.submit_job(job_hash, {'filename': data['filename']})
91
+
92
+ def on_print_complete(data):
93
+ client.complete_job(current_job_hash, user_wallet)
94
+ GRBL/CNC
95
+ javascript// On program start
96
+ const jobHash = client.generateJobHash(programName);
97
+ await client.submitJob(jobHash, {
98
+ job_type: 'cnc',
99
+ program: programName
100
+ });
101
+
102
+ // On program complete (M2/M30)
103
+ await client.completeJob(jobHash, wallet);
104
+ Custom/DIY
105
+ javascript// Your machine, your rules
106
+ const client = new FoundryClient({
107
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts'
108
+ });
109
+
110
+ await client.init({
111
+ type: 'custom',
112
+ model: 'Pick and Place Robot'
113
+ });
114
+
115
+ // When task starts
116
+ const jobHash = client.generateJobHash(taskId);
117
+ await client.submitJob(jobHash, {
118
+ job_type: 'robot',
119
+ task_id: taskId
120
+ });
121
+
122
+ // When task completes
123
+ await client.completeJob(jobHash, wallet);
124
+
125
+ API Reference
126
+ FoundryClient(config)
127
+ javascriptconst client = new FoundryClient({
128
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts',
129
+ retryAttempts: 3, // Network retry count
130
+ retryDelay: 2000, // ms between retries
131
+ debug: false // Enable verbose logging
132
+ });
133
+ client.init(metadata)
134
+ Initialize machine. Loads existing credentials or generates new ones.
135
+ javascriptawait client.init({
136
+ type: 'printer', // printer|cnc|robot|laser|custom
137
+ model: 'Ender 3 V2',
138
+ firmware: 'Klipper'
139
+ });
140
+ // Saves credentials to .foundry_credentials.json
141
+ Returns:
142
+ javascript{
143
+ existing: true, // Whether credentials already existed
144
+ machineUuid: "abc-123", // Your machine ID
145
+ identity: {...} // New credentials (if first time)
146
+ }
147
+ client.submitJob(jobHash, payload)
148
+ Register job start.
149
+ javascriptawait client.submitJob('job_unique_hash', {
150
+ job_type: 'print',
151
+ filename: 'part.gcode',
152
+ estimated_time: 3600
153
+ });
154
+ Returns:
155
+ javascript{
156
+ success: true,
157
+ job_hash: "job_unique_hash"
158
+ }
159
+ client.completeJob(jobHash, recipientWallet)
160
+ Complete job and earn MINT.
161
+ javascriptconst result = await client.completeJob(
162
+ 'job_unique_hash',
163
+ 'YOUR_SOLANA_WALLET_ADDRESS'
164
+ );
165
+ Returns:
166
+ javascript{
167
+ success: true,
168
+ reward: 3,
169
+ tx_signature: "5x7y...",
170
+ solscan: "https://solscan.io/tx/..."
171
+ }
172
+ client.generateJobHash(filename, additionalData)
173
+ Generate deterministic job hash.
174
+ javascriptconst jobHash = client.generateJobHash('benchy.gcode');
175
+ // Returns: "job_abc123_1234567890"
176
+
177
+ Rules & Limits
178
+
179
+ 3 MINT per completed job
180
+ 400 MINT maximum per machine per 24 hours
181
+ Jobs must run minimum 60 seconds
182
+ Each job hash must be unique (no replays)
183
+ Signature timestamp must be within 5 minutes
184
+
185
+ What Counts as a Job?
186
+ Any discrete unit of productive work:
187
+
188
+ One 3D print from start to finish
189
+ One CNC machining operation
190
+ One robot task cycle
191
+ One laser cut/engrave job
192
+
193
+ What Doesn't Count?
194
+
195
+ Machine idle time
196
+ Calibration/homing
197
+ Test runs
198
+ Failed or cancelled jobs
199
+
200
+
201
+ Security
202
+ Proof of Productivity (PoP)
203
+ Every job completion requires:
204
+
205
+ Unique job hash (prevents replay attacks)
206
+ Ed25519 signature from registered machine keypair
207
+ Recent timestamp (within 5 minutes)
208
+ Minimum duration (60 seconds)
209
+ Rate limiting (400 MINT/24hrs per machine)
210
+
211
+ Your Machine Keys
212
+
213
+ Generated locally on first init()
214
+ Stored in .foundry_credentials.json
215
+ Never leave your machine
216
+ Back them up securely
217
+
218
+ Anti-Fraud
219
+
220
+ Job hash uniqueness enforced
221
+ Signature verification prevents impersonation
222
+ Rate limits prevent spam
223
+ Duration checks prevent instant gaming
224
+
225
+
226
+ Troubleshooting
227
+ "Job completion failed"
228
+
229
+ Check your machine credentials are loaded (client.init())
230
+ Verify job was submitted before completing
231
+ Ensure wallet address is valid Solana address
232
+
233
+ "Rate limit exceeded"
234
+
235
+ You've earned 400 MINT in the last 24 hours
236
+ Wait for the rolling window to reset
237
+ Check dashboard for current limits
238
+
239
+ "Signature verification failed"
240
+
241
+ Machine credentials may be corrupted
242
+ Delete .foundry_credentials.json and re-run init()
243
+ Ensure system time is accurate (for timestamps)
244
+
245
+ "Job duration too short"
246
+
247
+ Jobs must run minimum 60 seconds
248
+ Ensure you're measuring actual work time
249
+ Don't complete jobs immediately after starting
250
+
251
+ "Treasury depleted"
252
+
253
+ System maintenance in progress
254
+ Jobs are queued and will process when treasury refills
255
+ Check status updates
256
+
257
+
258
+ Getting a Wallet?
259
+ New to crypto?
260
+ Here's how to receive MINT:
261
+
262
+ Install Phantom Wallet (browser extension)
263
+ Create new wallet
264
+ Copy your Solana address
265
+ Use that address in completeJob()
266
+
267
+ MINT tokens appear instantly in your wallet after job completion.
268
+
269
+ Vision
270
+ FoundryNet is building payment infrastructure for autonomous industry.
271
+ We're starting simple: machines prove work happened, get paid instantly. The protocol handles identity, verification, and settlement. Everything else such as quality systems, marketplaces, reputation—gets built on top.
272
+ This is infrastructure for when AI agents coordinate supply chains and manufacturing capacity becomes programmable.
273
+
274
+ Tokenomics
275
+ Token: MINT (Solana SPL token)
276
+ Contract: 84hLMyG9cnC3Pd9LrWn4NtKMEhQHh4AvxbkcDMQtgem
277
+
278
+
279
+ Initial treasury funded by protocol creator
280
+ Rewards distributed for completed jobs
281
+ LP available on Raydium (MINT/SOL)
@@ -0,0 +1,4 @@
1
+ from .client import FoundryClient
2
+
3
+ __version__ = "1.0.0"
4
+ __all__ = ["FoundryClient"]
@@ -0,0 +1,262 @@
1
+ import nacl.signing
2
+ import nacl.encoding
3
+ import base58
4
+ import requests
5
+ import json
6
+ import hashlib
7
+ import time
8
+ import os
9
+ from uuid import uuid4
10
+ from pathlib import Path
11
+ from datetime import datetime
12
+ from typing import Optional, Dict, Any
13
+
14
+ class FoundryClient:
15
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
16
+ config = config or {}
17
+ self.api_url = config.get('api_url', 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts')
18
+ self.retry_attempts = config.get('retry_attempts', 3)
19
+ self.retry_delay = config.get('retry_delay', 2.0)
20
+ self.debug = config.get('debug', False)
21
+ self.credentials_file = config.get('credentials_file', '.foundry_credentials.json')
22
+
23
+ self.machine_uuid: Optional[str] = None
24
+ self.signing_key: Optional[nacl.signing.SigningKey] = None
25
+ self.verify_key: Optional[nacl.signing.VerifyKey] = None
26
+
27
+ def log(self, level: str, message: str, data: Optional[Dict] = None):
28
+ if not self.debug and level == 'debug':
29
+ return
30
+ timestamp = datetime.utcnow().isoformat()
31
+ print(f"[FoundryNet {timestamp}] [{level.upper()}] {message}", data or {})
32
+
33
+ def _retry(self, fn, context: str = ''):
34
+ last_error = None
35
+ for attempt in range(1, self.retry_attempts + 1):
36
+ try:
37
+ return fn()
38
+ except Exception as error:
39
+ last_error = error
40
+ self.log('warn', f"{context} failed (attempt {attempt}/{self.retry_attempts})",
41
+ {'error': str(error)})
42
+ if attempt < self.retry_attempts:
43
+ delay = self.retry_delay * attempt
44
+ self.log('debug', f"Retrying in {delay}s...")
45
+ time.sleep(delay)
46
+
47
+ self.log('error', f"{context} failed after {self.retry_attempts} attempts",
48
+ {'error': str(last_error)})
49
+ raise last_error
50
+
51
+ def generate_machine_id(self) -> Dict[str, str]:
52
+ self.machine_uuid = str(uuid4())
53
+ self.signing_key = nacl.signing.SigningKey.generate()
54
+ self.verify_key = self.signing_key.verify_key
55
+
56
+ identity = {
57
+ 'machine_uuid': self.machine_uuid,
58
+ 'public_key': base58.b58encode(bytes(self.verify_key)).decode('utf-8'),
59
+ 'secret_key': base58.b58encode(bytes(self.signing_key)).decode('utf-8'),
60
+ }
61
+
62
+ self.log('info', 'Generated new machine identity', {
63
+ 'machine_uuid': identity['machine_uuid'],
64
+ 'public_key': identity['public_key'],
65
+ })
66
+ return identity
67
+
68
+ def load_machine_id(self, machine_uuid: str, secret_key_base58: str):
69
+ try:
70
+ self.machine_uuid = machine_uuid
71
+ secret_key_bytes = base58.b58decode(secret_key_base58)
72
+ self.signing_key = nacl.signing.SigningKey(secret_key_bytes)
73
+ self.verify_key = self.signing_key.verify_key
74
+ self.log('info', 'Loaded machine identity', {'machine_uuid': machine_uuid})
75
+ except Exception as error:
76
+ self.log('error', 'Failed to load machine identity', {'error': str(error)})
77
+ raise ValueError(f"Invalid machine credentials: {error}")
78
+
79
+ def save_credentials(self, identity: Dict[str, str]):
80
+ cred_path = Path.cwd() / self.credentials_file
81
+ credentials = {
82
+ 'machine_uuid': identity['machine_uuid'],
83
+ 'secret_key': identity['secret_key'],
84
+ 'public_key': identity['public_key'],
85
+ 'created_at': datetime.utcnow().isoformat(),
86
+ }
87
+ with open(cred_path, 'w') as f:
88
+ json.dump(credentials, f, indent=2)
89
+ self.log('debug', 'Credentials saved', {'file': str(cred_path)})
90
+
91
+ def load_credentials(self) -> bool:
92
+ try:
93
+ cred_path = Path.cwd() / self.credentials_file
94
+ if cred_path.exists():
95
+ with open(cred_path, 'r') as f:
96
+ creds = json.load(f)
97
+ self.load_machine_id(creds['machine_uuid'], creds['secret_key'])
98
+ self.log('info', 'Loaded existing credentials',
99
+ {'machine_uuid': self.machine_uuid})
100
+ return True
101
+ except Exception:
102
+ self.log('debug', 'No existing credentials found or corrupted')
103
+ return False
104
+
105
+ def init(self, metadata: Optional[Dict] = None) -> Dict[str, Any]:
106
+ metadata = metadata or {}
107
+
108
+ if self.load_credentials():
109
+ self.log('info', 'Using existing machine credentials',
110
+ {'machine_uuid': self.machine_uuid})
111
+ return {'existing': True, 'machine_uuid': self.machine_uuid}
112
+
113
+ identity = self.generate_machine_id()
114
+ self.save_credentials(identity)
115
+ self.register_machine(metadata)
116
+
117
+ return {
118
+ 'existing': False,
119
+ 'identity': identity,
120
+ 'machine_uuid': self.machine_uuid
121
+ }
122
+
123
+ def register_machine(self, metadata: Optional[Dict] = None) -> Dict:
124
+ metadata = metadata or {}
125
+
126
+ if not self.machine_uuid or not self.verify_key:
127
+ raise ValueError('Generate or load machine ID first')
128
+
129
+ def _register():
130
+ response = requests.post(
131
+ f"{self.api_url}/register-machine",
132
+ json={
133
+ 'machine_uuid': self.machine_uuid,
134
+ 'machine_pubkey_base58': base58.b58encode(bytes(self.verify_key)).decode('utf-8'),
135
+ 'metadata': metadata,
136
+ },
137
+ headers={'Content-Type': 'application/json'}
138
+ )
139
+
140
+ if not response.ok:
141
+ raise Exception(f"Registration failed: {response.text}")
142
+
143
+ result = response.json()
144
+ self.log('info', 'Machine registered successfully',
145
+ {'machine_uuid': self.machine_uuid})
146
+ return result
147
+
148
+ return self._retry(_register, 'Machine registration')
149
+
150
+ def submit_job(self, job_hash: str, complexity: float = 1.0,
151
+ payload: Optional[Dict] = None) -> Dict:
152
+ payload = payload or {}
153
+
154
+ if not self.machine_uuid:
155
+ raise ValueError('Machine not initialized')
156
+
157
+ normalized = round(complexity * 100) / 100
158
+
159
+ MIN_COMPLEXITY = 0.5
160
+ MAX_COMPLEXITY = 2.0
161
+ TOLERANCE = 0.01
162
+
163
+ if normalized < MIN_COMPLEXITY - TOLERANCE or normalized > MAX_COMPLEXITY + TOLERANCE:
164
+ raise ValueError(f"Complexity must be {MIN_COMPLEXITY}-{MAX_COMPLEXITY}, got {normalized}")
165
+
166
+ def _submit():
167
+ response = requests.post(
168
+ f"{self.api_url}/submit-job",
169
+ json={
170
+ 'machine_uuid': self.machine_uuid,
171
+ 'job_hash': job_hash,
172
+ 'complexity': normalized,
173
+ 'payload': payload,
174
+ },
175
+ headers={'Content-Type': 'application/json'}
176
+ )
177
+
178
+ if response.status_code == 409:
179
+ self.log('warn', 'Job already exists', {'job_hash': job_hash})
180
+ return {'success': True, 'duplicate': True, 'job_hash': job_hash}
181
+
182
+ if not response.ok:
183
+ text = response.text
184
+ try:
185
+ error = response.json().get('error', text)
186
+ except:
187
+ error = text
188
+ raise Exception(f"Job submission failed: {error}")
189
+
190
+ result = response.json()
191
+ self.log('debug', 'Job submitted', {'job_hash': job_hash, 'complexity': normalized})
192
+ return result
193
+
194
+ return self._retry(_submit, 'Job submission')
195
+
196
+ def complete_job(self, job_hash: str, recipient_wallet: str) -> Dict:
197
+ if not self.machine_uuid or not self.signing_key:
198
+ raise ValueError('Machine not initialized')
199
+
200
+ timestamp = datetime.utcnow().isoformat()
201
+ message = f"{job_hash}|{recipient_wallet}|{timestamp}"
202
+ message_bytes = message.encode('utf-8')
203
+
204
+ signed = self.signing_key.sign(message_bytes)
205
+ signature = signed.signature
206
+
207
+ def _complete():
208
+ response = requests.post(
209
+ f"{self.api_url}/complete-job",
210
+ json={
211
+ 'machine_uuid': self.machine_uuid,
212
+ 'job_hash': job_hash,
213
+ 'recipient_wallet': recipient_wallet,
214
+ 'completion_proof': {
215
+ 'timestamp': timestamp,
216
+ 'signature_base58': base58.b58encode(signature).decode('utf-8'),
217
+ },
218
+ },
219
+ headers={'Content-Type': 'application/json'}
220
+ )
221
+
222
+ if not response.ok:
223
+ raise Exception(f"Job completion failed: {response.text}")
224
+
225
+ result = response.json()
226
+ self.log('info', 'Job completed - MINT earned!', {
227
+ 'job_hash': job_hash,
228
+ 'reward': result.get('reward_net'),
229
+ 'tx_signature': result.get('tx_signature'),
230
+ 'activity_ratio': result.get('activity_ratio'),
231
+ })
232
+ return result
233
+
234
+ return self._retry(_complete, 'Job completion')
235
+
236
+ def generate_job_hash(self, filename: str, additional_data: str = '') -> str:
237
+ data = f"{self.machine_uuid}|{filename}|{int(time.time() * 1000)}|{additional_data}"
238
+ hash_obj = hashlib.sha256(data.encode('utf-8'))
239
+ hash_hex = hash_obj.hexdigest()
240
+ return f"job_{hash_hex[:16]}_{int(time.time() * 1000)}"
241
+
242
+ def get_metrics(self) -> Dict:
243
+ def _fetch():
244
+ response = requests.get(
245
+ f"{self.api_url}/metrics",
246
+ headers={'Content-Type': 'application/json'}
247
+ )
248
+
249
+ if not response.ok:
250
+ raise Exception("Failed to fetch metrics")
251
+
252
+ return response.json()
253
+
254
+ return self._retry(_fetch, 'Fetch metrics')
255
+
256
+ def get_machine_uuid(self) -> str:
257
+ return self.machine_uuid or ''
258
+
259
+ def get_public_key(self) -> str:
260
+ if self.verify_key:
261
+ return base58.b58encode(bytes(self.verify_key)).decode('utf-8')
262
+ return ''
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.4
2
+ Name: foundry-client
3
+ Version: 1.0.0
4
+ Summary: Python client for FoundryNet - Universal DePIN Protocol for Work Settlement
5
+ Home-page: https://github.com/foundrynet/foundry_net_MINT
6
+ Author: FoundryNet
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: PyNaCl>=1.5.0
19
+ Requires-Dist: base58>=2.1.1
20
+ Requires-Dist: requests>=2.31.0
21
+ Dynamic: author
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license-file
27
+ Dynamic: requires-dist
28
+ Dynamic: requires-python
29
+ Dynamic: summary
30
+
31
+ FoundryNet
32
+ Earn MINT tokens for verified manufacturing work.
33
+ FoundryNet rewards 3D printers, CNC machines, robots, and production equipment with cryptocurrency for completing jobs. No staking, no capital required—just connect your machine and start earning.
34
+
35
+ Quick Start
36
+ Installation
37
+ bashnpm install tweetnacl bs58 uuid
38
+ Download foundry-client.js from the GitHub repository.
39
+ Basic Usage
40
+ javascriptimport { FoundryClient } from './foundry-client.js';
41
+
42
+ // Initialize client
43
+ const client = new FoundryClient({
44
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts',
45
+ debug: true
46
+ });
47
+
48
+ // Register machine (first time only - saves credentials locally)
49
+ await client.init({
50
+ type: '3d-printer',
51
+ model: 'Ender 3'
52
+ });
53
+
54
+ // Start a job
55
+ const jobHash = client.generateJobHash('benchy.gcode');
56
+ await client.submitJob(jobHash, {
57
+ job_type: 'print',
58
+ filename: 'benchy.gcode'
59
+ });
60
+
61
+ // Complete job and earn MINT
62
+ const result = await client.completeJob(
63
+ jobHash,
64
+ 'YOUR_SOLANA_WALLET_ADDRESS'
65
+ );
66
+
67
+ console.log(`Earned ${result.reward} MINT!`);
68
+ console.log(`View transaction: ${result.solscan}`);
69
+ That's it. Your machine is now earning.
70
+
71
+ How It Works
72
+ 1. Register Your Machine
73
+ Connect any machine that can make HTTP requests and sign messages (ed25519).
74
+ Supported:
75
+
76
+ 3D Printers (OctoPrint, Klipper, Marlin)
77
+ CNC Mills/Routers (GRBL, LinuxCNC)
78
+ Laser Cutters
79
+ Pick-and-Place Robots
80
+ Custom DIY machines
81
+
82
+ 2. Submit Jobs
83
+ When your machine starts productive work:
84
+ javascriptconst jobHash = client.generateJobHash('my_part.gcode');
85
+ await client.submitJob(jobHash, {
86
+ job_type: 'print',
87
+ filename: 'benchy.gcode'
88
+ });
89
+ 3. Earn MINT
90
+ When work completes successfully:
91
+ javascriptawait client.completeJob(jobHash, yourSolanaWallet);
92
+ // Instant payout: 3 MINT per job
93
+ Current Rewards:
94
+
95
+ 3 MINT per completed job
96
+ Maximum 400 MINT per machine per 24 hours
97
+ Jobs must run minimum 60 seconds
98
+
99
+
100
+ Integration Examples
101
+ OctoPrint Plugin
102
+ pythonfrom foundrynet import FoundryClient
103
+
104
+ # In plugin initialization
105
+ client = FoundryClient(api_url='https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts')
106
+ client.init(metadata={'type': 'printer', 'model': 'Prusa MK3'})
107
+
108
+ # On print start
109
+ def on_print_started(filename):
110
+ job_hash = client.generate_job_hash(filename)
111
+ client.submit_job(job_hash, {'filename': filename})
112
+
113
+ # On print complete
114
+ def on_print_done(job_hash):
115
+ client.complete_job(job_hash, user_wallet)
116
+ Klipper/Moonraker
117
+ python# Listen to Moonraker webhooks
118
+ def on_print_started(data):
119
+ job_hash = client.generate_job_hash(data['filename'])
120
+ client.submit_job(job_hash, {'filename': data['filename']})
121
+
122
+ def on_print_complete(data):
123
+ client.complete_job(current_job_hash, user_wallet)
124
+ GRBL/CNC
125
+ javascript// On program start
126
+ const jobHash = client.generateJobHash(programName);
127
+ await client.submitJob(jobHash, {
128
+ job_type: 'cnc',
129
+ program: programName
130
+ });
131
+
132
+ // On program complete (M2/M30)
133
+ await client.completeJob(jobHash, wallet);
134
+ Custom/DIY
135
+ javascript// Your machine, your rules
136
+ const client = new FoundryClient({
137
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts'
138
+ });
139
+
140
+ await client.init({
141
+ type: 'custom',
142
+ model: 'Pick and Place Robot'
143
+ });
144
+
145
+ // When task starts
146
+ const jobHash = client.generateJobHash(taskId);
147
+ await client.submitJob(jobHash, {
148
+ job_type: 'robot',
149
+ task_id: taskId
150
+ });
151
+
152
+ // When task completes
153
+ await client.completeJob(jobHash, wallet);
154
+
155
+ API Reference
156
+ FoundryClient(config)
157
+ javascriptconst client = new FoundryClient({
158
+ apiUrl: 'https://lsijwmklicmqtuqxhgnu.supabase.co/functions/v1/main-ts',
159
+ retryAttempts: 3, // Network retry count
160
+ retryDelay: 2000, // ms between retries
161
+ debug: false // Enable verbose logging
162
+ });
163
+ client.init(metadata)
164
+ Initialize machine. Loads existing credentials or generates new ones.
165
+ javascriptawait client.init({
166
+ type: 'printer', // printer|cnc|robot|laser|custom
167
+ model: 'Ender 3 V2',
168
+ firmware: 'Klipper'
169
+ });
170
+ // Saves credentials to .foundry_credentials.json
171
+ Returns:
172
+ javascript{
173
+ existing: true, // Whether credentials already existed
174
+ machineUuid: "abc-123", // Your machine ID
175
+ identity: {...} // New credentials (if first time)
176
+ }
177
+ client.submitJob(jobHash, payload)
178
+ Register job start.
179
+ javascriptawait client.submitJob('job_unique_hash', {
180
+ job_type: 'print',
181
+ filename: 'part.gcode',
182
+ estimated_time: 3600
183
+ });
184
+ Returns:
185
+ javascript{
186
+ success: true,
187
+ job_hash: "job_unique_hash"
188
+ }
189
+ client.completeJob(jobHash, recipientWallet)
190
+ Complete job and earn MINT.
191
+ javascriptconst result = await client.completeJob(
192
+ 'job_unique_hash',
193
+ 'YOUR_SOLANA_WALLET_ADDRESS'
194
+ );
195
+ Returns:
196
+ javascript{
197
+ success: true,
198
+ reward: 3,
199
+ tx_signature: "5x7y...",
200
+ solscan: "https://solscan.io/tx/..."
201
+ }
202
+ client.generateJobHash(filename, additionalData)
203
+ Generate deterministic job hash.
204
+ javascriptconst jobHash = client.generateJobHash('benchy.gcode');
205
+ // Returns: "job_abc123_1234567890"
206
+
207
+ Rules & Limits
208
+
209
+ 3 MINT per completed job
210
+ 400 MINT maximum per machine per 24 hours
211
+ Jobs must run minimum 60 seconds
212
+ Each job hash must be unique (no replays)
213
+ Signature timestamp must be within 5 minutes
214
+
215
+ What Counts as a Job?
216
+ Any discrete unit of productive work:
217
+
218
+ One 3D print from start to finish
219
+ One CNC machining operation
220
+ One robot task cycle
221
+ One laser cut/engrave job
222
+
223
+ What Doesn't Count?
224
+
225
+ Machine idle time
226
+ Calibration/homing
227
+ Test runs
228
+ Failed or cancelled jobs
229
+
230
+
231
+ Security
232
+ Proof of Productivity (PoP)
233
+ Every job completion requires:
234
+
235
+ Unique job hash (prevents replay attacks)
236
+ Ed25519 signature from registered machine keypair
237
+ Recent timestamp (within 5 minutes)
238
+ Minimum duration (60 seconds)
239
+ Rate limiting (400 MINT/24hrs per machine)
240
+
241
+ Your Machine Keys
242
+
243
+ Generated locally on first init()
244
+ Stored in .foundry_credentials.json
245
+ Never leave your machine
246
+ Back them up securely
247
+
248
+ Anti-Fraud
249
+
250
+ Job hash uniqueness enforced
251
+ Signature verification prevents impersonation
252
+ Rate limits prevent spam
253
+ Duration checks prevent instant gaming
254
+
255
+
256
+ Troubleshooting
257
+ "Job completion failed"
258
+
259
+ Check your machine credentials are loaded (client.init())
260
+ Verify job was submitted before completing
261
+ Ensure wallet address is valid Solana address
262
+
263
+ "Rate limit exceeded"
264
+
265
+ You've earned 400 MINT in the last 24 hours
266
+ Wait for the rolling window to reset
267
+ Check dashboard for current limits
268
+
269
+ "Signature verification failed"
270
+
271
+ Machine credentials may be corrupted
272
+ Delete .foundry_credentials.json and re-run init()
273
+ Ensure system time is accurate (for timestamps)
274
+
275
+ "Job duration too short"
276
+
277
+ Jobs must run minimum 60 seconds
278
+ Ensure you're measuring actual work time
279
+ Don't complete jobs immediately after starting
280
+
281
+ "Treasury depleted"
282
+
283
+ System maintenance in progress
284
+ Jobs are queued and will process when treasury refills
285
+ Check status updates
286
+
287
+
288
+ Getting a Wallet?
289
+ New to crypto?
290
+ Here's how to receive MINT:
291
+
292
+ Install Phantom Wallet (browser extension)
293
+ Create new wallet
294
+ Copy your Solana address
295
+ Use that address in completeJob()
296
+
297
+ MINT tokens appear instantly in your wallet after job completion.
298
+
299
+ Vision
300
+ FoundryNet is building payment infrastructure for autonomous industry.
301
+ We're starting simple: machines prove work happened, get paid instantly. The protocol handles identity, verification, and settlement. Everything else such as quality systems, marketplaces, reputation—gets built on top.
302
+ This is infrastructure for when AI agents coordinate supply chains and manufacturing capacity becomes programmable.
303
+
304
+ Tokenomics
305
+ Token: MINT (Solana SPL token)
306
+ Contract: 84hLMyG9cnC3Pd9LrWn4NtKMEhQHh4AvxbkcDMQtgem
307
+
308
+
309
+ Initial treasury funded by protocol creator
310
+ Rewards distributed for completed jobs
311
+ LP available on Raydium (MINT/SOL)
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ foundry_client/__init__.py
5
+ foundry_client/client.py
6
+ foundry_client.egg-info/PKG-INFO
7
+ foundry_client.egg-info/SOURCES.txt
8
+ foundry_client.egg-info/dependency_links.txt
9
+ foundry_client.egg-info/requires.txt
10
+ foundry_client.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ PyNaCl>=1.5.0
2
+ base58>=2.1.1
3
+ requests>=2.31.0
@@ -0,0 +1 @@
1
+ foundry_client
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="foundry-client",
8
+ version="1.0.0",
9
+ author="FoundryNet",
10
+ description="Python client for FoundryNet - Universal DePIN Protocol for Work Settlement",
11
+ long_description=long_description,
12
+ long_description_content_type="text/markdown",
13
+ url="https://github.com/foundrynet/foundry_net_MINT",
14
+ packages=find_packages(),
15
+ classifiers=[
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ ],
25
+ python_requires=">=3.8",
26
+ install_requires=[
27
+ "PyNaCl>=1.5.0",
28
+ "base58>=2.1.1",
29
+ "requests>=2.31.0",
30
+ ],
31
+ )