apinow-sdk 0.12.5 → 0.12.6
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 +19 -136
- package/dist/index.d.ts +30 -10
- package/dist/index.js +31 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A TypeScript SDK for interacting with ApiNow endpoints, supporting Ethereum and
|
|
|
7
7
|
- **Automatic x402 Payments**: Intercepts `402` responses to handle payment flows automatically.
|
|
8
8
|
- **On-the-fly Token Swaps**: If you don't have the required payment token, the SDK can swap a common asset (like ETH, WETH, or USDC) to make the payment, powered by 0x.
|
|
9
9
|
- **Flexible Pricing**: Supports endpoints that require a fixed token amount or a USD equivalent.
|
|
10
|
+
- **Endpoint Discovery**: Includes a `search` method to find endpoints semantically.
|
|
10
11
|
- **Configurable Payment**: Prioritize which tokens you prefer to pay with.
|
|
11
12
|
- **Multi-chain support**: Works with Ethereum and Base.
|
|
12
13
|
- **Node.js Environment**: Designed to work in a Node.js environment.
|
|
@@ -19,33 +20,31 @@ npm install apinow-sdk
|
|
|
19
20
|
yarn add apinow-sdk
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
##
|
|
23
|
+
## Quick Example
|
|
23
24
|
|
|
24
25
|
The primary way to use the SDK is with the `execute` method. It's a single call that handles all the complexity of API payments for you.
|
|
25
26
|
|
|
26
27
|
```typescript
|
|
27
28
|
import apiNow from 'apinow-sdk';
|
|
28
29
|
|
|
29
|
-
// The API endpoint you want to interact with.
|
|
30
|
-
const ENDPOINT_URL = 'https://apinow.fun/api/endpoints/your-endpoint';
|
|
31
|
-
|
|
32
30
|
// Your private key, securely stored (e.g., in an environment variable).
|
|
33
|
-
const YOUR_WALLET_PRIVATE_KEY =
|
|
31
|
+
const YOUR_WALLET_PRIVATE_KEY = process.env.USER_PRIVATE_KEY;
|
|
34
32
|
|
|
35
33
|
async function main() {
|
|
36
34
|
try {
|
|
37
35
|
// The `execute` method handles everything automatically.
|
|
38
|
-
// If the API requires a payment (402), the SDK will
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
// 3. Send the payment transaction.
|
|
42
|
-
// 4. Retry the original request with proof of payment.
|
|
36
|
+
// If the API requires a payment (402), the SDK will find the best
|
|
37
|
+
// token you hold, swap if necessary, send the payment, and retry
|
|
38
|
+
// the original request with proof of payment.
|
|
43
39
|
const response = await apiNow.execute(
|
|
44
|
-
|
|
40
|
+
'https://apinow.fun/api/endpoints/apinowfun/translate-TRANSLATE',
|
|
45
41
|
YOUR_WALLET_PRIVATE_KEY,
|
|
46
42
|
{ // Optional: request options
|
|
47
43
|
method: 'POST',
|
|
48
|
-
data: {
|
|
44
|
+
data: {
|
|
45
|
+
text: 'Hello world',
|
|
46
|
+
selectedLanguage: 'es'
|
|
47
|
+
}
|
|
49
48
|
}
|
|
50
49
|
);
|
|
51
50
|
|
|
@@ -58,100 +57,28 @@ async function main() {
|
|
|
58
57
|
main();
|
|
59
58
|
```
|
|
60
59
|
|
|
60
|
+
For a complete, runnable example, see [`example.js`](./example.js).
|
|
61
|
+
|
|
61
62
|
## How It Works: Automatic Payments
|
|
62
63
|
|
|
63
64
|
When you call `execute`, the SDK makes a request to the endpoint. If the server responds with a `402 Payment Required` status, the SDK automatically performs the following steps:
|
|
64
65
|
|
|
65
|
-
1. **Parses Payment Options**: The `402` response contains a list of accepted payment options.
|
|
66
|
+
1. **Parses Payment Options**: The `402` response contains a list of accepted payment options.
|
|
66
67
|
2. **Checks Balances**: It checks your wallet balance for each of the accepted payment tokens.
|
|
67
68
|
3. **Prioritizes Payment**: It attempts to pay using your tokens in a preferred order (default: `['USDC', 'WETH', 'ETH']`).
|
|
68
|
-
4. **Swaps if Needed**: If you don't have any of the *required* tokens, the SDK will try to swap one of your preferred assets for the required one.
|
|
69
|
+
4. **Swaps if Needed**: If you don't have any of the *required* tokens, the SDK will try to swap one of your preferred assets for the required one.
|
|
69
70
|
5. **Pays and Retries**: Once the payment transaction is sent, the SDK automatically retries the original API request, now with proof of payment.
|
|
70
71
|
|
|
71
|
-
## Configuration
|
|
72
|
-
|
|
73
|
-
You can customize the behavior of the `execute` method with the `opts` and `paymentConfig` parameters.
|
|
74
|
-
|
|
75
|
-
### Request Options (`opts`)
|
|
76
|
-
|
|
77
|
-
Passed as the third argument to `execute`. This corresponds to `TxResponseOptions`.
|
|
78
|
-
|
|
79
|
-
- `method`: The HTTP method for your request (e.g., `'GET'`, `'POST'`). Defaults to `'GET'`.
|
|
80
|
-
- `data`: The payload for your request. For `POST` requests, this is the JSON body. For `GET`, it's converted to query parameters.
|
|
81
|
-
|
|
82
|
-
### Payment Configuration (`paymentConfig`)
|
|
83
|
-
|
|
84
|
-
Passed as the fourth argument to `execute`. This corresponds to `X402PaymentConfig`.
|
|
85
|
-
|
|
86
|
-
- `preferredTokens`: An array of token symbols (e.g., `['USDC', 'WETH']`) that you prefer to pay with. The SDK will check your balance of these tokens first.
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
await apiNow.execute(
|
|
90
|
-
ENDPOINT_URL,
|
|
91
|
-
YOUR_WALLET_PRIVATE_KEY,
|
|
92
|
-
{ method: 'POST', data: { /* ... */ } }, // opts
|
|
93
|
-
{ preferredTokens: ['DAI', 'USDC'] } // paymentConfig
|
|
94
|
-
);
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Legacy Flow (Backward Compatibility)
|
|
98
|
-
|
|
99
|
-
For backward compatibility, the `infoBuyResponse` method is still available. It performs a less sophisticated multi-step payment process.
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
const response = await apiNow.infoBuyResponse(
|
|
103
|
-
ENDPOINT_URL,
|
|
104
|
-
YOUR_WALLET_PRIVATE_KEY
|
|
105
|
-
);
|
|
106
|
-
```
|
|
107
|
-
|
|
108
72
|
## API Reference
|
|
109
73
|
|
|
110
74
|
### `execute(endpoint, privateKey, opts?, paymentConfig?)`
|
|
111
75
|
Handles a request and its potential payment in a single, automatic call. This is the recommended method.
|
|
112
76
|
|
|
113
|
-
### `
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
### `info(endpoint)`
|
|
117
|
-
(Legacy) Gets payment requirement information from an endpoint.
|
|
118
|
-
|
|
119
|
-
### `buy(walletAddress, amount, privateKey, chain, ...)`
|
|
120
|
-
(Legacy) Sends a payment transaction.
|
|
121
|
-
|
|
122
|
-
### `txResponse(endpoint, txHash, opts?)`
|
|
123
|
-
(Legacy) Fetches the API response after a payment has been made manually.
|
|
77
|
+
### `search(params, privateKey, paymentConfig?)`
|
|
78
|
+
Performs a semantic search for endpoints.
|
|
124
79
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
// Response from a 402 error
|
|
129
|
-
interface X402PaymentInfo {
|
|
130
|
-
challenge: string;
|
|
131
|
-
chain: 'eth' | 'base';
|
|
132
|
-
recipientAddress: string;
|
|
133
|
-
options: X402PaymentOption[];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// A single way to pay
|
|
137
|
-
interface X402PaymentOption {
|
|
138
|
-
tokenAddress: string;
|
|
139
|
-
symbol: string;
|
|
140
|
-
amount: string;
|
|
141
|
-
decimals: number;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Configuration for payments
|
|
145
|
-
interface X402PaymentConfig {
|
|
146
|
-
preferredTokens?: string[];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Options for the API request itself
|
|
150
|
-
interface TxResponseOptions {
|
|
151
|
-
method?: string;
|
|
152
|
-
data?: any;
|
|
153
|
-
}
|
|
154
|
-
```
|
|
80
|
+
### `info(endpointUrl)`
|
|
81
|
+
Retrieves public, detailed information about an endpoint.
|
|
155
82
|
|
|
156
83
|
## Default RPC URLs
|
|
157
84
|
|
|
@@ -179,47 +106,3 @@ It is NOT directly compatible with browsers or edge environments that do not pro
|
|
|
179
106
|
## License
|
|
180
107
|
|
|
181
108
|
MIT
|
|
182
|
-
|
|
183
|
-
## Examples
|
|
184
|
-
|
|
185
|
-
This project includes a test server and a test runner to demonstrate various payment scenarios.
|
|
186
|
-
|
|
187
|
-
1. **Create a `.env` file** in the root of the project and add your wallet's private key:
|
|
188
|
-
```
|
|
189
|
-
PRIVATE_KEY=your_private_key_here
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
2. **Install dependencies:**
|
|
193
|
-
```bash
|
|
194
|
-
npm install
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
3. **Start the test server:**
|
|
198
|
-
The test server simulates an API that requires different types of payments.
|
|
199
|
-
```bash
|
|
200
|
-
node test/test-server.js
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
4. **Run the test runner:**
|
|
204
|
-
In a separate terminal, run the test runner to execute a series of transactions against the test server.
|
|
205
|
-
```bash
|
|
206
|
-
node test/test-runner.js
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
This will demonstrate:
|
|
210
|
-
- Paying with USDC
|
|
211
|
-
- Paying with a custom ERC20 token
|
|
212
|
-
- Paying with a token priced in USDC
|
|
213
|
-
- Fallback token payments
|
|
214
|
-
- Handling various error conditions
|
|
215
|
-
|
|
216
|
-
## Local Development
|
|
217
|
-
|
|
218
|
-
1. **Build the project:**
|
|
219
|
-
```bash
|
|
220
|
-
npm run build
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
This will compile the TypeScript source files into JavaScript in the `dist` directory.
|
|
224
|
-
|
|
225
|
-
## Contributing
|
package/dist/index.d.ts
CHANGED
|
@@ -4,13 +4,35 @@ interface TxResponseOptions {
|
|
|
4
4
|
data?: any;
|
|
5
5
|
signature?: string;
|
|
6
6
|
}
|
|
7
|
-
interface
|
|
8
|
-
|
|
7
|
+
interface PaymentOption {
|
|
8
|
+
tokenAddress: string;
|
|
9
|
+
symbol: string;
|
|
10
|
+
amount?: string;
|
|
11
|
+
usdAmount?: string;
|
|
12
|
+
decimals: number;
|
|
13
|
+
}
|
|
14
|
+
interface EndpointInfo {
|
|
15
|
+
namespace: string;
|
|
16
|
+
endpointName: string;
|
|
17
|
+
description: string;
|
|
9
18
|
walletAddress: string;
|
|
19
|
+
paymentOptions: PaymentOption[];
|
|
10
20
|
httpMethod: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
querySchema?: any;
|
|
22
|
+
docsUrl?: string;
|
|
23
|
+
status: string;
|
|
24
|
+
chain: 'eth' | 'sol' | 'base';
|
|
25
|
+
url: string;
|
|
26
|
+
lastTx?: {
|
|
27
|
+
txHash: string;
|
|
28
|
+
chain: 'eth' | 'sol';
|
|
29
|
+
timestamp: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
interface SearchParams {
|
|
33
|
+
query: string;
|
|
34
|
+
limit?: number;
|
|
35
|
+
minScore?: number;
|
|
14
36
|
}
|
|
15
37
|
interface X402PaymentOption {
|
|
16
38
|
tokenAddress: string;
|
|
@@ -36,16 +58,14 @@ interface X402PaymentConfig {
|
|
|
36
58
|
declare function get0xSwapQuote(buyToken: string, sellToken: string, buyAmount: bigint, buyTokenDecimals: number, takerAddress: string, chain: 'eth' | 'base', slippagePercentage?: string): Promise<any>;
|
|
37
59
|
declare class ApiNow {
|
|
38
60
|
private handlers;
|
|
39
|
-
info(
|
|
61
|
+
info(endpointUrl: string): Promise<EndpointInfo>;
|
|
62
|
+
search(params: SearchParams, userWalletPrivateKey: string, paymentConfig?: X402PaymentConfig): Promise<any>;
|
|
40
63
|
buy(walletAddress: string, amount: bigint, userWalletPrivateKey: string, chain: 'eth' | 'base', rpcUrl?: string, tokenAddress?: string, fastMode?: boolean): Promise<string>;
|
|
41
64
|
execute(endpoint: string, userWalletPrivateKey: string, opts?: TxResponseOptions, paymentConfig?: X402PaymentConfig): Promise<any>;
|
|
42
65
|
approve(wallet: Wallet, tokenAddress: string, spenderAddress: string, amount: bigint): Promise<string>;
|
|
43
66
|
txResponse(endpoint: string, txHash: string, opts?: TxResponseOptions): Promise<any>;
|
|
44
|
-
infoBuyResponse(endpoint: string, userWalletPrivateKey: string, rpcUrl?: string, opts?: TxResponseOptions & {
|
|
45
|
-
fastMode?: boolean;
|
|
46
|
-
}): Promise<any>;
|
|
47
67
|
}
|
|
48
68
|
declare const apiNow: ApiNow;
|
|
49
69
|
export default apiNow;
|
|
50
70
|
export { get0xSwapQuote };
|
|
51
|
-
export type {
|
|
71
|
+
export type { TxResponseOptions, X402PaymentInfo, X402PaymentOption, X402PaymentConfig, EndpointInfo, SearchParams, PaymentOption };
|
package/dist/index.js
CHANGED
|
@@ -519,18 +519,42 @@ class ApiNow {
|
|
|
519
519
|
base: new EthereumHandler()
|
|
520
520
|
};
|
|
521
521
|
}
|
|
522
|
-
async info(
|
|
523
|
-
if (!
|
|
524
|
-
throw new Error('Invalid endpoint URL');
|
|
525
|
-
}
|
|
522
|
+
async info(endpointUrl) {
|
|
523
|
+
if (!endpointUrl || typeof endpointUrl !== 'string') {
|
|
524
|
+
throw new Error('Invalid endpoint URL provided.');
|
|
525
|
+
}
|
|
526
|
+
// Ensure the URL doesn't already end in /details before appending
|
|
527
|
+
const detailsUrl = endpointUrl.endsWith('/details')
|
|
528
|
+
? endpointUrl
|
|
529
|
+
: `${endpointUrl}/details`;
|
|
530
|
+
console.log(`[sdk] Fetching public info from: ${detailsUrl}`);
|
|
526
531
|
try {
|
|
527
|
-
|
|
532
|
+
// Using a simple fetch as this is a public, non-paying endpoint
|
|
533
|
+
const response = await fetch(detailsUrl);
|
|
534
|
+
if (!response.ok) {
|
|
535
|
+
const errorBody = await response.text();
|
|
536
|
+
throw new Error(`Failed to fetch endpoint info (${response.status}): ${errorBody}`);
|
|
537
|
+
}
|
|
538
|
+
return await response.json();
|
|
528
539
|
}
|
|
529
540
|
catch (error) {
|
|
530
|
-
console.error(`
|
|
531
|
-
|
|
541
|
+
console.error(`[sdk] Error in info():`, error);
|
|
542
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
543
|
+
throw new Error(`Could not get endpoint info: ${message}`);
|
|
532
544
|
}
|
|
533
545
|
}
|
|
546
|
+
async search(params, userWalletPrivateKey, paymentConfig = {}) {
|
|
547
|
+
const searchEndpoint = 'https://apinow.fun/api/endpoints/apinowfun/endpoint-search';
|
|
548
|
+
console.log(`[sdk] Performing semantic search with query: "${params.query}"`);
|
|
549
|
+
if (!params.query || typeof params.query !== 'string' || params.query.trim() === '') {
|
|
550
|
+
throw new Error('A non-empty search query string is required.');
|
|
551
|
+
}
|
|
552
|
+
// Reuse the existing execute method to handle the x402 payment flow
|
|
553
|
+
return this.execute(searchEndpoint, userWalletPrivateKey, {
|
|
554
|
+
method: 'POST',
|
|
555
|
+
data: params, // The search parameters become the body of the POST request
|
|
556
|
+
}, paymentConfig);
|
|
557
|
+
}
|
|
534
558
|
async buy(walletAddress, amount, userWalletPrivateKey, chain, rpcUrl, tokenAddress, fastMode) {
|
|
535
559
|
const handler = this.handlers[chain];
|
|
536
560
|
if (!handler) {
|
|
@@ -643,48 +667,6 @@ class ApiNow {
|
|
|
643
667
|
throw new Error(`Could not get transaction response: ${error instanceof Error ? error.message : String(error)}`);
|
|
644
668
|
}
|
|
645
669
|
}
|
|
646
|
-
async infoBuyResponse(endpoint, userWalletPrivateKey, rpcUrl, opts = {}) {
|
|
647
|
-
console.error(`Starting infoBuyResponse for endpoint: ${endpoint}`);
|
|
648
|
-
const info = await this.info(endpoint);
|
|
649
|
-
console.error("Received info:", info);
|
|
650
|
-
const { requiredAmount, walletAddress, chain, tokenAddress, decimals } = info;
|
|
651
|
-
if (!chain || !this.handlers[chain]) {
|
|
652
|
-
throw new Error(`Unsupported chain specified by endpoint: ${chain}`);
|
|
653
|
-
}
|
|
654
|
-
let amountBigInt;
|
|
655
|
-
try {
|
|
656
|
-
// Use info.decimals if available, otherwise default to 18 (for ETH)
|
|
657
|
-
const parseDecimals = (tokenAddress && decimals !== undefined) ? decimals : 18;
|
|
658
|
-
amountBigInt = parseUnits(requiredAmount, parseDecimals);
|
|
659
|
-
if (amountBigInt <= 0n) {
|
|
660
|
-
throw new Error('Required amount must be positive.');
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
catch (e) {
|
|
664
|
-
throw new Error(`Invalid requiredAmount format or value: ${requiredAmount}. Could not parse with ${(tokenAddress && decimals !== undefined) ? decimals : 18} decimals.`);
|
|
665
|
-
}
|
|
666
|
-
console.error(`Attempting payment: Chain=${chain}, To=${walletAddress}, Amount=${amountBigInt.toString()}, Token=${tokenAddress || 'Native'}`);
|
|
667
|
-
const txHash = await this.buy(walletAddress, amountBigInt, userWalletPrivateKey, chain, rpcUrl, tokenAddress, opts.fastMode);
|
|
668
|
-
console.error(`Transaction sent: ${txHash}`);
|
|
669
|
-
const wallet = new Wallet(userWalletPrivateKey);
|
|
670
|
-
const signature = await wallet.signMessage(txHash);
|
|
671
|
-
console.error(`Generated signature for txHash ${txHash}: ${signature}`);
|
|
672
|
-
if (!opts.fastMode) {
|
|
673
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
677
|
-
}
|
|
678
|
-
console.error(`Fetching response for tx: ${txHash}`);
|
|
679
|
-
// Create specific options for txResponse
|
|
680
|
-
const txResponseOpts = {
|
|
681
|
-
method: info.httpMethod || 'POST', // Use the method from info, default to POST
|
|
682
|
-
data: opts.data, // Pass the original data payload intended for the API
|
|
683
|
-
signature: signature
|
|
684
|
-
};
|
|
685
|
-
// Call txResponse with the tailored options
|
|
686
|
-
return this.txResponse(endpoint, txHash, txResponseOpts);
|
|
687
|
-
}
|
|
688
670
|
}
|
|
689
671
|
const apiNow = new ApiNow();
|
|
690
672
|
export default apiNow;
|