apinow-sdk 0.2.0 → 0.4.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/README.md +108 -51
- package/dist/index.d.ts +6 -2
- package/dist/index.js +138 -17
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# ApiNow SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A TypeScript SDK for interacting with ApiNow endpoints, supporting Ethereum (including Base), and Solana chains.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Multi-chain support (Ethereum, Base, Solana)
|
|
8
|
+
- Token transfers (ERC20 on ETH/Base, SPL on Solana)
|
|
9
|
+
- Fast mode for quicker transaction processing
|
|
10
|
+
- TypeScript types for better development experience
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
@@ -8,78 +15,128 @@ The endpoint vending machine - SDK for interacting with ApiNow endpoints. This i
|
|
|
8
15
|
npm install apinow-sdk
|
|
9
16
|
```
|
|
10
17
|
|
|
11
|
-
##
|
|
18
|
+
## Usage
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
### Basic Example
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
14
23
|
import apiNow from 'apinow-sdk';
|
|
15
24
|
|
|
16
|
-
//
|
|
25
|
+
// Get endpoint info
|
|
26
|
+
const info = await apiNow.info('https://apinow.fun/api/endpoints/your-endpoint');
|
|
27
|
+
|
|
28
|
+
// Send payment and get response
|
|
17
29
|
const response = await apiNow.infoBuyResponse(
|
|
18
|
-
'https://apinow.fun/api/endpoints/
|
|
19
|
-
'
|
|
20
|
-
'
|
|
30
|
+
'https://apinow.fun/api/endpoints/your-endpoint',
|
|
31
|
+
'YOUR_PRIVATE_KEY',
|
|
32
|
+
'YOUR_RPC_URL'
|
|
21
33
|
);
|
|
22
34
|
```
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
### Fast Mode
|
|
25
37
|
|
|
26
|
-
|
|
27
|
-
Fetches endpoint metadata like required ETH amount and wallet address.
|
|
38
|
+
Fast mode skips transaction confirmation and only waits for the transaction to be in the mempool. This provides much faster responses but slightly less security:
|
|
28
39
|
|
|
29
|
-
```
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
```typescript
|
|
41
|
+
const response = await apiNow.infoBuyResponse(
|
|
42
|
+
'https://apinow.fun/api/endpoints/your-endpoint',
|
|
43
|
+
'YOUR_PRIVATE_KEY',
|
|
44
|
+
'YOUR_RPC_URL',
|
|
45
|
+
{ fastMode: true }
|
|
46
|
+
);
|
|
36
47
|
```
|
|
37
48
|
|
|
38
|
-
###
|
|
39
|
-
|
|
49
|
+
### Chain-Specific Examples
|
|
50
|
+
|
|
51
|
+
#### Ethereum/Base
|
|
40
52
|
|
|
41
|
-
```
|
|
53
|
+
```typescript
|
|
54
|
+
// Native ETH/BASE transfer
|
|
42
55
|
const txHash = await apiNow.buy(
|
|
43
|
-
|
|
44
|
-
ethers.parseEther(
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
'RECIPIENT_ADDRESS',
|
|
57
|
+
ethers.parseEther('0.1'),
|
|
58
|
+
'YOUR_PRIVATE_KEY',
|
|
59
|
+
'YOUR_RPC_URL',
|
|
60
|
+
'eth'
|
|
47
61
|
);
|
|
48
|
-
```
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
method: "POST", // Optional, defaults to GET
|
|
59
|
-
data: { foo: "bar" } // Optional request body
|
|
60
|
-
}
|
|
63
|
+
// ERC20 token transfer
|
|
64
|
+
const txHash = await apiNow.buy(
|
|
65
|
+
'RECIPIENT_ADDRESS',
|
|
66
|
+
ethers.parseUnits('100', 18), // Use appropriate decimals
|
|
67
|
+
'YOUR_PRIVATE_KEY',
|
|
68
|
+
'YOUR_RPC_URL',
|
|
69
|
+
'eth',
|
|
70
|
+
'TOKEN_ADDRESS'
|
|
61
71
|
);
|
|
62
72
|
```
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
Combines all steps: fetches info, sends payment, and gets response.
|
|
74
|
+
#### Solana
|
|
66
75
|
|
|
67
|
-
```
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
```typescript
|
|
77
|
+
// Native SOL transfer
|
|
78
|
+
const txHash = await apiNow.buy(
|
|
79
|
+
'RECIPIENT_ADDRESS',
|
|
80
|
+
BigInt(LAMPORTS_PER_SOL), // 1 SOL
|
|
81
|
+
'YOUR_PRIVATE_KEY',
|
|
82
|
+
'YOUR_RPC_URL',
|
|
83
|
+
'sol'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// SPL token transfer
|
|
87
|
+
const txHash = await apiNow.buy(
|
|
88
|
+
'RECIPIENT_ADDRESS',
|
|
89
|
+
BigInt(1000000), // Amount in raw units (e.g. 1.0 for 6 decimals)
|
|
90
|
+
'YOUR_PRIVATE_KEY',
|
|
91
|
+
'YOUR_RPC_URL',
|
|
92
|
+
'sol',
|
|
93
|
+
'TOKEN_ADDRESS'
|
|
80
94
|
);
|
|
81
95
|
```
|
|
82
96
|
|
|
97
|
+
## API Reference
|
|
98
|
+
|
|
99
|
+
### `info(endpoint: string): Promise<InfoResponse>`
|
|
100
|
+
|
|
101
|
+
Gets information about an endpoint.
|
|
102
|
+
|
|
103
|
+
### `buy(walletAddress: string, amount: bigint, pkey: string, rpc: string, chain?: 'eth' | 'sol', tokenAddress?: string, fastMode?: boolean): Promise<string>`
|
|
104
|
+
|
|
105
|
+
Sends a payment transaction. For tokens, provide the amount in raw units (e.g. wei for ERC20, raw units for SPL).
|
|
106
|
+
|
|
107
|
+
### `txResponse(endpoint: string, txHash: string, opts?: TxResponseOptions): Promise<any>`
|
|
108
|
+
|
|
109
|
+
Gets the API response for a transaction.
|
|
110
|
+
|
|
111
|
+
### `infoBuyResponse(endpoint: string, pkey: string, rpc: string, opts?: TxResponseOptions & { fastMode?: boolean }): Promise<any>`
|
|
112
|
+
|
|
113
|
+
Combines info, buy, and txResponse into a single call.
|
|
114
|
+
|
|
115
|
+
## Types
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface TxResponseOptions {
|
|
119
|
+
method?: string;
|
|
120
|
+
data?: any;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface InfoResponse {
|
|
124
|
+
requiredAmount: string;
|
|
125
|
+
walletAddress: string;
|
|
126
|
+
httpMethod: string;
|
|
127
|
+
tokenAddress?: string;
|
|
128
|
+
chain: 'eth' | 'sol';
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Error Handling
|
|
133
|
+
|
|
134
|
+
The SDK throws descriptive errors for various failure cases:
|
|
135
|
+
- Invalid endpoint URLs
|
|
136
|
+
- Transaction failures
|
|
137
|
+
- Network issues
|
|
138
|
+
- Invalid addresses or amounts
|
|
139
|
+
|
|
83
140
|
## License
|
|
84
141
|
|
|
85
142
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -7,12 +7,16 @@ interface InfoResponse {
|
|
|
7
7
|
walletAddress: string;
|
|
8
8
|
httpMethod: string;
|
|
9
9
|
tokenAddress?: string;
|
|
10
|
+
chain: 'eth' | 'sol';
|
|
10
11
|
}
|
|
11
12
|
declare class ApiNow {
|
|
13
|
+
private handlers;
|
|
12
14
|
info(endpoint: string): Promise<InfoResponse>;
|
|
13
|
-
buy(walletAddress: string, amount: bigint, pkey: string, rpc: string, tokenAddress?: string): Promise<string>;
|
|
15
|
+
buy(walletAddress: string, amount: bigint, pkey: string, rpc: string, chain?: 'eth' | 'sol', tokenAddress?: string, fastMode?: boolean): Promise<string>;
|
|
14
16
|
txResponse(endpoint: string, txHash: string, opts?: TxResponseOptions): Promise<any>;
|
|
15
|
-
infoBuyResponse(endpoint: string, pkey: string, rpc: string, opts?: TxResponseOptions
|
|
17
|
+
infoBuyResponse(endpoint: string, pkey: string, rpc: string, opts?: TxResponseOptions & {
|
|
18
|
+
fastMode?: boolean;
|
|
19
|
+
}): Promise<any>;
|
|
16
20
|
}
|
|
17
21
|
declare const _default: ApiNow;
|
|
18
22
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -5,19 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const ethers_1 = require("ethers");
|
|
7
7
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
console.log(`Fetching info from ${endpoint}`);
|
|
14
|
-
const response = await (0, node_fetch_1.default)(`${endpoint}`);
|
|
15
|
-
if (!response.ok) {
|
|
16
|
-
throw new Error(`Failed to fetch endpoint info: ${response.status}`);
|
|
17
|
-
}
|
|
18
|
-
return response.json();
|
|
19
|
-
}
|
|
20
|
-
async buy(walletAddress, amount, pkey, rpc, tokenAddress) {
|
|
8
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
9
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
10
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
11
|
+
class EthereumHandler {
|
|
12
|
+
async buy(walletAddress, amount, pkey, rpc, tokenAddress, fastMode) {
|
|
21
13
|
if (!rpc || typeof rpc !== 'string') {
|
|
22
14
|
throw new Error('Invalid RPC URL');
|
|
23
15
|
}
|
|
@@ -35,14 +27,12 @@ class ApiNow {
|
|
|
35
27
|
? 30000
|
|
36
28
|
: 21000;
|
|
37
29
|
if (tokenAddress) {
|
|
38
|
-
// ERC20 transfer
|
|
39
30
|
const abi = ["function transfer(address to, uint256 amount) returns (bool)"];
|
|
40
31
|
const tokenContract = new ethers_1.ethers.Contract(tokenAddress, abi, wallet);
|
|
41
32
|
const tx = await tokenContract.transfer(walletAddress, amount);
|
|
42
33
|
return tx.hash;
|
|
43
34
|
}
|
|
44
35
|
else {
|
|
45
|
-
// Native ETH transfer
|
|
46
36
|
const tx = await wallet.sendTransaction({
|
|
47
37
|
to: walletAddress,
|
|
48
38
|
value: amount,
|
|
@@ -60,6 +50,135 @@ class ApiNow {
|
|
|
60
50
|
throw new Error(`Transaction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
61
51
|
}
|
|
62
52
|
}
|
|
53
|
+
}
|
|
54
|
+
class SolanaHandler {
|
|
55
|
+
async sendWithRetry(connection, transaction, senderKeypair, fastMode, maxAttempts = 3) {
|
|
56
|
+
let lastError;
|
|
57
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
58
|
+
try {
|
|
59
|
+
console.log(`\nAttempt ${attempt}/${maxAttempts}`);
|
|
60
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('processed');
|
|
61
|
+
console.log('Got blockhash:', blockhash.slice(0, 10) + '...');
|
|
62
|
+
transaction.recentBlockhash = blockhash;
|
|
63
|
+
transaction.lastValidBlockHeight = lastValidBlockHeight;
|
|
64
|
+
transaction.feePayer = senderKeypair.publicKey;
|
|
65
|
+
transaction.signatures = [];
|
|
66
|
+
transaction.sign(senderKeypair);
|
|
67
|
+
const rawTx = transaction.serialize();
|
|
68
|
+
const signature = await connection.sendRawTransaction(rawTx, {
|
|
69
|
+
skipPreflight: false,
|
|
70
|
+
maxRetries: 1,
|
|
71
|
+
preflightCommitment: 'processed'
|
|
72
|
+
});
|
|
73
|
+
console.log('Transaction sent! Signature:', signature);
|
|
74
|
+
if (!fastMode) {
|
|
75
|
+
console.log('Waiting for confirmation...');
|
|
76
|
+
let confirmationAttempt = 1;
|
|
77
|
+
while (confirmationAttempt <= 5) {
|
|
78
|
+
try {
|
|
79
|
+
console.log(`Confirmation attempt ${confirmationAttempt}/5`);
|
|
80
|
+
await connection.confirmTransaction({
|
|
81
|
+
signature,
|
|
82
|
+
blockhash,
|
|
83
|
+
lastValidBlockHeight
|
|
84
|
+
}, 'processed');
|
|
85
|
+
console.log('Transaction confirmed!');
|
|
86
|
+
return signature;
|
|
87
|
+
}
|
|
88
|
+
catch (confirmError) {
|
|
89
|
+
const errorMessage = confirmError instanceof Error
|
|
90
|
+
? confirmError.message
|
|
91
|
+
: 'Unknown confirmation error';
|
|
92
|
+
console.log('Confirmation failed:', errorMessage);
|
|
93
|
+
if (confirmationAttempt === 5)
|
|
94
|
+
throw confirmError;
|
|
95
|
+
confirmationAttempt++;
|
|
96
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return signature;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const errorMessage = error instanceof Error
|
|
104
|
+
? error.message
|
|
105
|
+
: 'Unknown error';
|
|
106
|
+
console.log('Attempt failed:', errorMessage);
|
|
107
|
+
lastError = error;
|
|
108
|
+
if (attempt < maxAttempts) {
|
|
109
|
+
console.log('Waiting 500ms before retry...');
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw lastError;
|
|
115
|
+
}
|
|
116
|
+
async buy(walletAddress, amount, pkey, rpc, tokenAddress, fastMode) {
|
|
117
|
+
const connection = new web3_js_1.Connection(rpc, {
|
|
118
|
+
commitment: 'processed',
|
|
119
|
+
confirmTransactionInitialTimeout: 10000
|
|
120
|
+
});
|
|
121
|
+
try {
|
|
122
|
+
const recipientPubkey = new web3_js_1.PublicKey(walletAddress);
|
|
123
|
+
const senderKeypair = web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(pkey));
|
|
124
|
+
const transaction = new web3_js_1.Transaction();
|
|
125
|
+
// Add priority fee instruction
|
|
126
|
+
transaction.add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
|
|
127
|
+
microLamports: 50000
|
|
128
|
+
}));
|
|
129
|
+
if (tokenAddress) {
|
|
130
|
+
const mint = new web3_js_1.PublicKey(tokenAddress);
|
|
131
|
+
const senderATA = await (0, spl_token_1.getAssociatedTokenAddress)(mint, senderKeypair.publicKey);
|
|
132
|
+
const recipientATA = await (0, spl_token_1.getAssociatedTokenAddress)(mint, recipientPubkey);
|
|
133
|
+
// Get token decimals
|
|
134
|
+
const mintInfo = await (0, spl_token_1.getMint)(connection, mint);
|
|
135
|
+
console.log('Token decimals:', mintInfo.decimals);
|
|
136
|
+
console.log('Original amount:', amount.toString());
|
|
137
|
+
// Don't multiply by decimals since amount is already raw
|
|
138
|
+
transaction.add((0, spl_token_1.createTransferInstruction)(senderATA, recipientATA, senderKeypair.publicKey, Number(amount) // Use raw amount directly
|
|
139
|
+
));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// SOL transfer
|
|
143
|
+
transaction.add(web3_js_1.SystemProgram.transfer({
|
|
144
|
+
fromPubkey: senderKeypair.publicKey,
|
|
145
|
+
toPubkey: recipientPubkey,
|
|
146
|
+
lamports: Number(amount)
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
return await this.sendWithRetry(connection, transaction, senderKeypair, !!fastMode);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('Detailed error:', error);
|
|
153
|
+
throw new Error(`Transaction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
class ApiNow {
|
|
158
|
+
constructor() {
|
|
159
|
+
this.handlers = {
|
|
160
|
+
eth: new EthereumHandler(),
|
|
161
|
+
sol: new SolanaHandler()
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async info(endpoint) {
|
|
165
|
+
if (!endpoint.startsWith('https://apinow.fun/api/endpoints/')) {
|
|
166
|
+
throw new Error('Invalid endpoint URL format');
|
|
167
|
+
}
|
|
168
|
+
console.log(`Fetching info from ${endpoint}`);
|
|
169
|
+
const response = await (0, node_fetch_1.default)(`${endpoint}`);
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
throw new Error(`Failed to fetch endpoint info: ${response.status}`);
|
|
172
|
+
}
|
|
173
|
+
return response.json();
|
|
174
|
+
}
|
|
175
|
+
async buy(walletAddress, amount, pkey, rpc, chain = 'eth', tokenAddress, fastMode) {
|
|
176
|
+
const handler = this.handlers[chain];
|
|
177
|
+
if (!handler) {
|
|
178
|
+
throw new Error(`Unsupported chain: ${chain}`);
|
|
179
|
+
}
|
|
180
|
+
return handler.buy(walletAddress, amount, pkey, rpc, tokenAddress, fastMode);
|
|
181
|
+
}
|
|
63
182
|
async txResponse(endpoint, txHash, opts = {}) {
|
|
64
183
|
if (!endpoint.startsWith('https://apinow.fun/api/endpoints/')) {
|
|
65
184
|
throw new Error('Invalid endpoint URL format');
|
|
@@ -81,8 +200,10 @@ class ApiNow {
|
|
|
81
200
|
}
|
|
82
201
|
async infoBuyResponse(endpoint, pkey, rpc, opts = {}) {
|
|
83
202
|
const info = await this.info(endpoint);
|
|
84
|
-
const amount =
|
|
85
|
-
|
|
203
|
+
const amount = info.chain === 'sol'
|
|
204
|
+
? BigInt(Math.round(Number(info.requiredAmount) * web3_js_1.LAMPORTS_PER_SOL))
|
|
205
|
+
: ethers_1.ethers.parseEther(info.requiredAmount);
|
|
206
|
+
const txHash = await this.buy(info.walletAddress, amount, pkey, rpc, info.chain, info.tokenAddress, opts.fastMode);
|
|
86
207
|
console.log('infoBuyResponse:', { endpoint, txHash, ...opts });
|
|
87
208
|
const response = await this.txResponse(endpoint, txHash, {
|
|
88
209
|
method: opts.method || info.httpMethod,
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apinow-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "ApiNow SDK · The endpoint vending machine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
7
8
|
"files": [
|
|
8
9
|
"dist"
|
|
9
10
|
],
|
|
@@ -20,6 +21,9 @@
|
|
|
20
21
|
"author": "Your Name",
|
|
21
22
|
"license": "MIT",
|
|
22
23
|
"dependencies": {
|
|
24
|
+
"@solana/spl-token": "^0.4.12",
|
|
25
|
+
"@solana/web3.js": "^1.98.0",
|
|
26
|
+
"bs58": "^6.0.0",
|
|
23
27
|
"ethers": "^6.13.5",
|
|
24
28
|
"node-fetch": "^3.3.2"
|
|
25
29
|
},
|