@satelink/sdk 0.2.0

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.
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@satelink/sdk",
3
+ "version": "0.2.0",
4
+ "description": "Official SDK for the Satelink DePIN network — RPC, MEV relay, AI inference, and blockchain tools",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.cjs",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./rpc": {
14
+ "import": "./dist/rpc.js",
15
+ "types": "./dist/rpc.d.ts"
16
+ },
17
+ "./mev": {
18
+ "import": "./dist/mev.js",
19
+ "types": "./dist/mev.d.ts"
20
+ },
21
+ "./adapters": {
22
+ "import": "./dist/adapters.js",
23
+ "types": "./dist/adapters.d.ts"
24
+ },
25
+ "./ai": {
26
+ "import": "./dist/ai.js",
27
+ "types": "./dist/ai.d.ts"
28
+ }
29
+ },
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsc --watch",
33
+ "lint": "echo 'Skipped (TypeScript handles linting)'",
34
+ "test": "vitest"
35
+ },
36
+ "keywords": [
37
+ "satelink",
38
+ "depin",
39
+ "rpc",
40
+ "ethereum",
41
+ "polygon",
42
+ "mev",
43
+ "flashbots",
44
+ "blockchain",
45
+ "ai",
46
+ "openai",
47
+ "ethers",
48
+ "viem",
49
+ "wagmi"
50
+ ],
51
+ "author": "Satelink Network",
52
+ "license": "MIT",
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/Satelink-Protocol/Satelink_Network.git"
56
+ },
57
+ "homepage": "https://satelink.network",
58
+ "devDependencies": {
59
+ "typescript": "^5.4.0"
60
+ },
61
+ "peerDependencies": {
62
+ "ethers": ">=5.0.0",
63
+ "viem": ">=1.0.0",
64
+ "wagmi": ">=2.0.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "ethers": { "optional": true },
68
+ "viem": { "optional": true },
69
+ "wagmi": { "optional": true }
70
+ }
71
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Framework Adapters
3
+ * L8-002: Drop-in adapters for ethers.js, wagmi, and viem
4
+ *
5
+ * Makes Satelink RPC a drop-in replacement for Alchemy/Infura
6
+ */
7
+
8
+ const BASE_URL = 'https://rpc.satelink.network';
9
+
10
+ export const SATELINK_CHAINS = {
11
+ polygon: { chainId: 137, name: 'Polygon', rpcUrl: `${BASE_URL}/rpc/polygon` },
12
+ ethereum: { chainId: 1, name: 'Ethereum', rpcUrl: `${BASE_URL}/rpc/ethereum` },
13
+ arbitrum: { chainId: 42161, name: 'Arbitrum One', rpcUrl: `${BASE_URL}/rpc/arbitrum` },
14
+ base: { chainId: 8453, name: 'Base', rpcUrl: `${BASE_URL}/rpc/base` },
15
+ amoy: { chainId: 80002, name: 'Polygon Amoy', rpcUrl: `${BASE_URL}/rpc/amoy` }
16
+ } as const;
17
+
18
+ export type SatelinkChain = keyof typeof SATELINK_CHAINS;
19
+
20
+ /**
21
+ * Get the RPC URL for a chain
22
+ */
23
+ export function getSatelinkRpcUrl(chain: SatelinkChain = 'polygon', apiKey?: string): string {
24
+ const config = SATELINK_CHAINS[chain];
25
+ return apiKey ? `${config.rpcUrl}?apiKey=${apiKey}` : config.rpcUrl;
26
+ }
27
+
28
+ /**
29
+ * Create an ethers.js v6 JsonRpcProvider for Satelink
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { getEthersProvider } from '@satelink/sdk';
34
+ * const provider = getEthersProvider('polygon');
35
+ * const block = await provider.getBlockNumber();
36
+ * ```
37
+ */
38
+ export function getEthersProvider(
39
+ chain: SatelinkChain = 'polygon',
40
+ apiKey?: string
41
+ ): unknown {
42
+ const config = SATELINK_CHAINS[chain];
43
+ const url = config.rpcUrl;
44
+
45
+ try {
46
+ // Try ethers v6 first
47
+ const { JsonRpcProvider } = require('ethers');
48
+ const fetchRequest = apiKey ? {
49
+ url,
50
+ getUrlFunc: () => url,
51
+ processFunc: (req: unknown, resp: unknown) => resp,
52
+ setHeader: (key: string, value: string) => {},
53
+ } : undefined;
54
+
55
+ return new JsonRpcProvider(url, config.chainId, {
56
+ staticNetwork: true
57
+ });
58
+ } catch {
59
+ try {
60
+ // Fall back to ethers v5
61
+ const { providers } = require('ethers');
62
+ const connection = apiKey
63
+ ? { url, headers: { 'X-API-Key': apiKey } }
64
+ : url;
65
+ return new providers.JsonRpcProvider(connection, config.chainId);
66
+ } catch {
67
+ throw new Error(
68
+ '@satelink/sdk ethers adapter requires ethers ^5.0.0 or ^6.0.0. ' +
69
+ 'Install with: npm install ethers'
70
+ );
71
+ }
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Create a viem http transport for Satelink
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { createPublicClient } from 'viem';
81
+ * import { polygon } from 'viem/chains';
82
+ * import { getViemTransport } from '@satelink/sdk';
83
+ *
84
+ * const client = createPublicClient({
85
+ * chain: polygon,
86
+ * transport: getViemTransport('polygon'),
87
+ * });
88
+ * ```
89
+ */
90
+ export function getViemTransport(
91
+ chain: SatelinkChain = 'polygon',
92
+ apiKey?: string
93
+ ): unknown {
94
+ const config = SATELINK_CHAINS[chain];
95
+
96
+ try {
97
+ const { http } = require('viem');
98
+ return http(config.rpcUrl, {
99
+ fetchOptions: apiKey
100
+ ? { headers: { 'X-API-Key': apiKey } }
101
+ : undefined,
102
+ retryCount: 3,
103
+ retryDelay: 150,
104
+ timeout: 20000
105
+ });
106
+ } catch {
107
+ throw new Error(
108
+ '@satelink/sdk viem adapter requires viem ^1.0.0 or ^2.0.0. ' +
109
+ 'Install with: npm install viem'
110
+ );
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Get a wagmi-compatible chain config for Satelink RPC
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * import { createConfig, http } from 'wagmi';
120
+ * import { getSatelinkChainConfig } from '@satelink/sdk';
121
+ *
122
+ * const config = createConfig({
123
+ * chains: [getSatelinkChainConfig('polygon')],
124
+ * transports: { 137: http('https://rpc.satelink.network/rpc/polygon') }
125
+ * });
126
+ * ```
127
+ */
128
+ export function getSatelinkChainConfig(chain: SatelinkChain = 'polygon') {
129
+ const config = SATELINK_CHAINS[chain];
130
+
131
+ return {
132
+ id: config.chainId,
133
+ name: config.name,
134
+ nativeCurrency: getNativeCurrency(chain),
135
+ rpcUrls: {
136
+ default: { http: [config.rpcUrl] },
137
+ public: { http: [config.rpcUrl] }
138
+ },
139
+ blockExplorers: getBlockExplorer(chain)
140
+ };
141
+ }
142
+
143
+ function getNativeCurrency(chain: SatelinkChain) {
144
+ switch (chain) {
145
+ case 'polygon':
146
+ case 'amoy':
147
+ return { name: 'MATIC', symbol: 'MATIC', decimals: 18 };
148
+ case 'ethereum':
149
+ return { name: 'Ether', symbol: 'ETH', decimals: 18 };
150
+ case 'arbitrum':
151
+ return { name: 'Ether', symbol: 'ETH', decimals: 18 };
152
+ case 'base':
153
+ return { name: 'Ether', symbol: 'ETH', decimals: 18 };
154
+ default:
155
+ return { name: 'Ether', symbol: 'ETH', decimals: 18 };
156
+ }
157
+ }
158
+
159
+ function getBlockExplorer(chain: SatelinkChain) {
160
+ const explorers: Record<SatelinkChain, { default: { name: string; url: string } }> = {
161
+ polygon: { default: { name: 'Polygonscan', url: 'https://polygonscan.com' } },
162
+ ethereum: { default: { name: 'Etherscan', url: 'https://etherscan.io' } },
163
+ arbitrum: { default: { name: 'Arbiscan', url: 'https://arbiscan.io' } },
164
+ base: { default: { name: 'Basescan', url: 'https://basescan.org' } },
165
+ amoy: { default: { name: 'Polygonscan Amoy', url: 'https://amoy.polygonscan.com' } }
166
+ };
167
+ return explorers[chain];
168
+ }
169
+
170
+ /**
171
+ * Raw JSON-RPC call to Satelink (no dependencies)
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import { rpc } from '@satelink/sdk';
176
+ * const blockNumber = await rpc('eth_blockNumber', [], 'polygon');
177
+ * ```
178
+ */
179
+ export async function rpc<T = unknown>(
180
+ method: string,
181
+ params: unknown[] = [],
182
+ chain: SatelinkChain = 'polygon',
183
+ apiKey?: string
184
+ ): Promise<T> {
185
+ const config = SATELINK_CHAINS[chain];
186
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
187
+ if (apiKey) headers['X-API-Key'] = apiKey;
188
+
189
+ const response = await fetch(config.rpcUrl, {
190
+ method: 'POST',
191
+ headers,
192
+ body: JSON.stringify({
193
+ jsonrpc: '2.0',
194
+ id: Date.now(),
195
+ method,
196
+ params
197
+ })
198
+ });
199
+
200
+ const data = await response.json();
201
+
202
+ if (data.error) {
203
+ throw new Error(`RPC error: ${data.error.message} (code: ${data.error.code})`);
204
+ }
205
+
206
+ return data.result as T;
207
+ }
package/src/ai.ts ADDED
@@ -0,0 +1,100 @@
1
+ import type {
2
+ SatelinkAIOptions,
3
+ ChatMessage,
4
+ ChatCompletionOptions,
5
+ ChatCompletionResponse,
6
+ Model
7
+ } from './types.js';
8
+
9
+ const DEFAULT_BASE_URL = 'https://rpc.satelink.network';
10
+
11
+ export class SatelinkAI {
12
+ private apiKey: string;
13
+ private baseUrl: string;
14
+
15
+ constructor(options: SatelinkAIOptions) {
16
+ if (!options.apiKey) {
17
+ throw new Error('API key is required for SatelinkAI');
18
+ }
19
+ this.apiKey = options.apiKey;
20
+ this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;
21
+ }
22
+
23
+ private getHeaders(): Record<string, string> {
24
+ return {
25
+ 'Content-Type': 'application/json',
26
+ 'x-api-key': this.apiKey
27
+ };
28
+ }
29
+
30
+ async chat(
31
+ messages: ChatMessage[],
32
+ options: ChatCompletionOptions = {}
33
+ ): Promise<ChatCompletionResponse> {
34
+ const body = {
35
+ model: options.model || 'satelink-default',
36
+ messages,
37
+ max_tokens: options.max_tokens || 1000,
38
+ temperature: options.temperature,
39
+ stream: false
40
+ };
41
+
42
+ const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
43
+ method: 'POST',
44
+ headers: this.getHeaders(),
45
+ body: JSON.stringify(body)
46
+ });
47
+
48
+ if (!response.ok) {
49
+ const error = await response.json().catch(() => ({}));
50
+ throw new Error(
51
+ `AI request failed: ${response.status} - ${error.error?.message || response.statusText}`
52
+ );
53
+ }
54
+
55
+ return response.json();
56
+ }
57
+
58
+ async complete(prompt: string, options: ChatCompletionOptions = {}): Promise<string> {
59
+ const body = {
60
+ model: options.model || 'satelink-default',
61
+ prompt,
62
+ max_tokens: options.max_tokens || 100,
63
+ temperature: options.temperature
64
+ };
65
+
66
+ const response = await fetch(`${this.baseUrl}/v1/completions`, {
67
+ method: 'POST',
68
+ headers: this.getHeaders(),
69
+ body: JSON.stringify(body)
70
+ });
71
+
72
+ if (!response.ok) {
73
+ const error = await response.json().catch(() => ({}));
74
+ throw new Error(
75
+ `AI request failed: ${response.status} - ${error.error?.message || response.statusText}`
76
+ );
77
+ }
78
+
79
+ const data = await response.json();
80
+ return data.choices?.[0]?.text || '';
81
+ }
82
+
83
+ async getModels(): Promise<Model[]> {
84
+ const response = await fetch(`${this.baseUrl}/v1/models`, {
85
+ method: 'GET',
86
+ headers: this.getHeaders()
87
+ });
88
+
89
+ if (!response.ok) {
90
+ throw new Error(`Failed to fetch models: ${response.status}`);
91
+ }
92
+
93
+ const data = await response.json();
94
+ return data.data || [];
95
+ }
96
+
97
+ get models(): Promise<Model[]> {
98
+ return this.getModels();
99
+ }
100
+ }
package/src/index.ts ADDED
@@ -0,0 +1,43 @@
1
+ // Core RPC client
2
+ export { SatelinkRPC, createProvider } from './rpc.js';
3
+
4
+ // EIP-1193 Provider
5
+ export { SatelinkProvider, createSatelinkProvider } from './provider.js';
6
+
7
+ // AI inference client
8
+ export { SatelinkAI } from './ai.js';
9
+
10
+ // MEV relay client
11
+ export { SatelinkMEV, createMevClient } from './mev.js';
12
+
13
+ // Framework adapters (ethers.js, viem, wagmi)
14
+ export {
15
+ SATELINK_CHAINS,
16
+ getSatelinkRpcUrl,
17
+ getEthersProvider,
18
+ getViemTransport,
19
+ getSatelinkChainConfig,
20
+ rpc
21
+ } from './adapters.js';
22
+
23
+ // Types
24
+ export type {
25
+ SatelinkRPCOptions,
26
+ SatelinkAIOptions,
27
+ JsonRpcRequest,
28
+ JsonRpcResponse,
29
+ ChatMessage,
30
+ ChatCompletionOptions,
31
+ ChatCompletionResponse,
32
+ Model,
33
+ TransactionRequest
34
+ } from './types.js';
35
+ export type { EIP1193Provider } from './rpc.js';
36
+ export type { SatelinkProviderOptions } from './provider.js';
37
+ export type { SatelinkChain } from './adapters.js';
38
+ export type {
39
+ MevClientOptions,
40
+ MevSubmitResult,
41
+ MevSimulationResult,
42
+ MevBundleStatus
43
+ } from './mev.js';
package/src/mev.ts ADDED
@@ -0,0 +1,213 @@
1
+ /**
2
+ * MEV Relay Client
3
+ * L8-002: Satelink MEV private relay for searchers
4
+ *
5
+ * Features:
6
+ * - Private transaction submission
7
+ * - Bundle submission (Flashbots-compatible)
8
+ * - Bundle simulation (eth_callBundle)
9
+ * - Bundle status tracking
10
+ */
11
+
12
+ export interface MevSubmitResult {
13
+ ok: boolean;
14
+ txHash?: string;
15
+ bundleHash?: string;
16
+ provider?: string;
17
+ requestId?: string;
18
+ priceUsdt?: number;
19
+ error?: string;
20
+ }
21
+
22
+ export interface MevSimulationResult {
23
+ ok: boolean;
24
+ simulation?: {
25
+ bundleHash?: string;
26
+ coinbaseDiff?: string;
27
+ ethSentToCoinbase?: string;
28
+ gasFees?: string;
29
+ totalGasUsed?: number;
30
+ results?: unknown[];
31
+ };
32
+ profitable?: boolean;
33
+ latency_ms?: number;
34
+ fee_usdt?: number;
35
+ error?: string;
36
+ requestId?: string;
37
+ }
38
+
39
+ export interface MevBundleStatus {
40
+ ok: boolean;
41
+ bundleHash: string;
42
+ status: 'pending' | 'pending_high_priority' | 'not_found' | 'unknown';
43
+ stats?: {
44
+ isSimulated?: boolean;
45
+ isSentToMiners?: boolean;
46
+ isHighPriority?: boolean;
47
+ simulatedAt?: string;
48
+ submittedAt?: string;
49
+ sentToMinersAt?: string;
50
+ };
51
+ fee_usdt?: number;
52
+ error?: string;
53
+ }
54
+
55
+ export interface MevClientOptions {
56
+ apiKey: string;
57
+ baseUrl?: string;
58
+ chain?: 'ethereum' | 'polygon' | 'arbitrum';
59
+ }
60
+
61
+ const DEFAULT_BASE_URL = 'https://rpc.satelink.network';
62
+
63
+ export class SatelinkMEV {
64
+ private apiKey: string;
65
+ private baseUrl: string;
66
+ private chain: string;
67
+
68
+ constructor(options: MevClientOptions) {
69
+ if (!options.apiKey) {
70
+ throw new Error('MEV relay requires an API key');
71
+ }
72
+ this.apiKey = options.apiKey;
73
+ this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;
74
+ this.chain = options.chain || 'ethereum';
75
+ }
76
+
77
+ private getHeaders(): Record<string, string> {
78
+ return {
79
+ 'Content-Type': 'application/json',
80
+ 'X-API-Key': this.apiKey
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Submit a signed transaction to the private mempool
86
+ * Transaction is NOT broadcast publicly — goes directly to validators
87
+ */
88
+ async submitPrivateTransaction(signedTx: string): Promise<MevSubmitResult> {
89
+ const response = await fetch(`${this.baseUrl}/rpc/mev?chain=${this.chain}`, {
90
+ method: 'POST',
91
+ headers: this.getHeaders(),
92
+ body: JSON.stringify({
93
+ jsonrpc: '2.0',
94
+ id: Date.now(),
95
+ method: 'eth_sendRawTransaction',
96
+ params: [signedTx]
97
+ })
98
+ });
99
+
100
+ const data = await response.json();
101
+
102
+ if (data.error) {
103
+ return { ok: false, error: data.error.message || data.error };
104
+ }
105
+
106
+ return {
107
+ ok: true,
108
+ txHash: data.result,
109
+ provider: data._mev?.provider,
110
+ requestId: data._mev?.requestId,
111
+ priceUsdt: data._mev?.priceUsdt
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Submit a bundle of transactions (Flashbots-compatible)
117
+ * All transactions execute atomically or none do
118
+ */
119
+ async submitBundle(
120
+ txs: string[],
121
+ options: {
122
+ blockNumber?: string | number;
123
+ minTimestamp?: number;
124
+ maxTimestamp?: number;
125
+ } = {}
126
+ ): Promise<MevSubmitResult> {
127
+ const response = await fetch(`${this.baseUrl}/rpc/mev/bundle?chain=${this.chain}`, {
128
+ method: 'POST',
129
+ headers: this.getHeaders(),
130
+ body: JSON.stringify({
131
+ txs,
132
+ blockNumber: options.blockNumber,
133
+ minTimestamp: options.minTimestamp,
134
+ maxTimestamp: options.maxTimestamp
135
+ })
136
+ });
137
+
138
+ const data = await response.json();
139
+
140
+ if (!data.ok) {
141
+ return { ok: false, error: data.error || data.message };
142
+ }
143
+
144
+ return {
145
+ ok: true,
146
+ bundleHash: data.bundleHash,
147
+ provider: data.provider,
148
+ requestId: data.requestId,
149
+ priceUsdt: data.priceUsdt
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Simulate a bundle without submitting (eth_callBundle)
155
+ * Use this to check profitability before paying for submission
156
+ */
157
+ async simulateBundle(
158
+ txs: string[],
159
+ options: {
160
+ blockNumber?: string;
161
+ stateBlockNumber?: string;
162
+ } = {}
163
+ ): Promise<MevSimulationResult> {
164
+ const response = await fetch(`${this.baseUrl}/rpc/mev/bundle/simulate`, {
165
+ method: 'POST',
166
+ headers: this.getHeaders(),
167
+ body: JSON.stringify({
168
+ txs,
169
+ blockNumber: options.blockNumber,
170
+ stateBlockNumber: options.stateBlockNumber
171
+ })
172
+ });
173
+
174
+ return response.json();
175
+ }
176
+
177
+ /**
178
+ * Check if a bundle was included on-chain
179
+ */
180
+ async getBundleStatus(bundleHash: string, blockNumber?: string): Promise<MevBundleStatus> {
181
+ const url = new URL(`${this.baseUrl}/rpc/mev/bundle/${bundleHash}`);
182
+ if (blockNumber) url.searchParams.set('blockNumber', blockNumber);
183
+
184
+ const response = await fetch(url.toString(), {
185
+ method: 'GET',
186
+ headers: this.getHeaders()
187
+ });
188
+
189
+ return response.json();
190
+ }
191
+
192
+ /**
193
+ * Get MEV relay status and stats
194
+ */
195
+ async getStatus(): Promise<unknown> {
196
+ const response = await fetch(`${this.baseUrl}/rpc/mev/status`);
197
+ return response.json();
198
+ }
199
+
200
+ /**
201
+ * Set the target chain for MEV operations
202
+ */
203
+ setChain(chain: 'ethereum' | 'polygon' | 'arbitrum'): void {
204
+ this.chain = chain;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Create a Satelink MEV client
210
+ */
211
+ export function createMevClient(options: MevClientOptions): SatelinkMEV {
212
+ return new SatelinkMEV(options);
213
+ }