apinow-sdk 0.9.0 → 0.11.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 +38 -40
- package/dist/index.d.ts +3 -6
- package/dist/index.js +24 -163
- package/package.json +1 -3
package/README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# ApiNow SDK
|
|
2
2
|
|
|
3
|
-
A TypeScript SDK for interacting with ApiNow endpoints, supporting Ethereum
|
|
3
|
+
A TypeScript SDK for interacting with ApiNow endpoints, supporting Ethereum and Base chains. Designed to work in Node.js, browsers, and edge environments like Cloudflare Workers.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- Multi-chain support (Ethereum, Base
|
|
8
|
-
- Native and
|
|
7
|
+
- Multi-chain support (Ethereum, Base)
|
|
8
|
+
- Native and ERC20 token transfers
|
|
9
9
|
- Environment Agnostic: Uses global `fetch` for broad compatibility.
|
|
10
10
|
- Optional RPC URL: Uses public RPCs by default, allows override.
|
|
11
|
-
- Fast mode for quicker transaction processing (skips confirmation wait).
|
|
12
11
|
- TypeScript types for better development experience.
|
|
13
12
|
|
|
14
13
|
## Installation
|
|
@@ -27,20 +26,21 @@ yarn add apinow-sdk
|
|
|
27
26
|
import apiNow from 'apinow-sdk';
|
|
28
27
|
|
|
29
28
|
const ENDPOINT_URL = 'https://apinow.fun/api/endpoints/your-endpoint';
|
|
30
|
-
const
|
|
29
|
+
const YOUR_WALLET_PRIVATE_KEY = '0x...';
|
|
31
30
|
|
|
32
31
|
// 1. Get endpoint info (payment details)
|
|
33
32
|
const info = await apiNow.info(ENDPOINT_URL);
|
|
34
33
|
// info will contain: { requiredAmount, walletAddress, httpMethod, tokenAddress, chain }
|
|
34
|
+
// Note: chain will be 'eth' or 'base'
|
|
35
35
|
console.log('Payment required:', info);
|
|
36
36
|
|
|
37
37
|
// 2. Send payment and get the API response in one step
|
|
38
38
|
try {
|
|
39
39
|
const response = await apiNow.infoBuyResponse(
|
|
40
40
|
ENDPOINT_URL,
|
|
41
|
-
|
|
41
|
+
YOUR_WALLET_PRIVATE_KEY
|
|
42
42
|
// Optional: Add RPC URL override here if needed
|
|
43
|
-
// Optional: Add options like { fastMode: true } here
|
|
43
|
+
// Optional: Add options like { fastMode: true } here (Note: fastMode currently has no effect)
|
|
44
44
|
);
|
|
45
45
|
console.log('API Response:', response);
|
|
46
46
|
} catch (error) {
|
|
@@ -50,64 +50,61 @@ try {
|
|
|
50
50
|
|
|
51
51
|
### Providing a Custom RPC URL
|
|
52
52
|
|
|
53
|
-
If you need to use a specific RPC node:
|
|
53
|
+
If you need to use a specific RPC node (e.g., for Base):
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
|
-
const CUSTOM_RPC_URL = 'https://
|
|
56
|
+
const CUSTOM_RPC_URL = 'https://mainnet.base.org';
|
|
57
57
|
|
|
58
58
|
const response = await apiNow.infoBuyResponse(
|
|
59
59
|
ENDPOINT_URL,
|
|
60
|
-
|
|
60
|
+
YOUR_WALLET_PRIVATE_KEY,
|
|
61
61
|
CUSTOM_RPC_URL // Provide the RPC URL here
|
|
62
62
|
);
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
### Fast Mode
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
*Note: `fastMode` is currently included for potential future use but does not affect Ethereum/Base transaction confirmation behavior in this version.*
|
|
68
68
|
|
|
69
69
|
```typescript
|
|
70
70
|
const response = await apiNow.infoBuyResponse(
|
|
71
71
|
ENDPOINT_URL,
|
|
72
|
-
|
|
72
|
+
YOUR_WALLET_PRIVATE_KEY,
|
|
73
73
|
undefined, // Use default RPC
|
|
74
|
-
{ fastMode: true } //
|
|
74
|
+
{ fastMode: true } // Option included, but no effect yet
|
|
75
75
|
);
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
### Manual Payment (Separate Steps)
|
|
79
79
|
|
|
80
|
-
You can also perform the payment manually if needed.
|
|
81
|
-
|
|
82
80
|
```typescript
|
|
83
81
|
import apiNow from 'apinow-sdk';
|
|
84
|
-
import { ethers } from 'ethers';
|
|
82
|
+
import { ethers } from 'ethers';
|
|
85
83
|
|
|
86
84
|
const ENDPOINT_URL = 'https://apinow.fun/api/endpoints/your-endpoint';
|
|
87
|
-
const
|
|
85
|
+
const YOUR_WALLET_PRIVATE_KEY = '0x...';
|
|
88
86
|
const YOUR_CUSTOM_RPC_URL = 'https://your-node.com'; // Optional
|
|
89
87
|
|
|
90
88
|
// 1. Get Info
|
|
91
89
|
const info = await apiNow.info(ENDPOINT_URL);
|
|
92
90
|
const { requiredAmount, walletAddress, chain, tokenAddress } = info;
|
|
93
91
|
|
|
94
|
-
// Convert requiredAmount (string) to bigint (
|
|
92
|
+
// Convert requiredAmount (string) to bigint (wei)
|
|
95
93
|
const amountBigInt = BigInt(requiredAmount);
|
|
96
94
|
|
|
97
95
|
// 2. Send Payment
|
|
98
96
|
const txHash = await apiNow.buy(
|
|
99
97
|
walletAddress,
|
|
100
98
|
amountBigInt,
|
|
101
|
-
|
|
102
|
-
chain, //
|
|
99
|
+
YOUR_WALLET_PRIVATE_KEY,
|
|
100
|
+
chain, // 'eth' or 'base'
|
|
103
101
|
YOUR_CUSTOM_RPC_URL, // Optional: override RPC
|
|
104
|
-
tokenAddress
|
|
105
|
-
|
|
102
|
+
tokenAddress // Optional: specify ERC20 token if required
|
|
103
|
+
// fastMode param exists but is unused by handler
|
|
106
104
|
);
|
|
107
105
|
console.log(`Payment sent: ${txHash}`);
|
|
108
106
|
|
|
109
|
-
// 3. Get API Response (
|
|
110
|
-
// Add a delay here if needed
|
|
107
|
+
// 3. Get API Response (add delay as needed)
|
|
111
108
|
await new Promise(resolve => setTimeout(resolve, 5000)); // Example 5s delay
|
|
112
109
|
|
|
113
110
|
const apiResponse = await apiNow.txResponse(
|
|
@@ -123,14 +120,15 @@ console.log('API Response:', apiResponse);
|
|
|
123
120
|
|
|
124
121
|
Gets payment requirement information about an ApiNow endpoint.
|
|
125
122
|
|
|
126
|
-
### `buy(walletAddress: string, amount: bigint,
|
|
123
|
+
### `buy(walletAddress: string, amount: bigint, userWalletPrivateKey: string, chain: 'eth' | 'base', rpcUrl?: string, tokenAddress?: string, fastMode?: boolean): Promise<string>`
|
|
127
124
|
|
|
128
125
|
Sends the required payment transaction to the specified address.
|
|
129
|
-
- `amount`: The required amount in the smallest unit (wei
|
|
130
|
-
- `
|
|
126
|
+
- `amount`: The required amount in the smallest unit (wei).
|
|
127
|
+
- `userWalletPrivateKey`: The private key of the wallet sending the funds.
|
|
128
|
+
- `chain`: The blockchain target ('eth' or 'base').
|
|
131
129
|
- `rpcUrl` (Optional): Overrides the default public RPC URL.
|
|
132
|
-
- `tokenAddress` (Optional): The contract address if paying with an ERC20
|
|
133
|
-
- `fastMode` (Optional):
|
|
130
|
+
- `tokenAddress` (Optional): The contract address if paying with an ERC20 token.
|
|
131
|
+
- `fastMode` (Optional): Parameter exists but currently has no effect.
|
|
134
132
|
|
|
135
133
|
Returns the transaction hash.
|
|
136
134
|
|
|
@@ -138,15 +136,16 @@ Returns the transaction hash.
|
|
|
138
136
|
|
|
139
137
|
Fetches the final API response from the endpoint after a successful payment.
|
|
140
138
|
- `txHash`: The hash of the payment transaction.
|
|
141
|
-
- `opts` (Optional): Options like `{ method: 'POST', data: {...} }
|
|
139
|
+
- `opts` (Optional): Options like `{ method: 'POST', data: {...} }`.
|
|
142
140
|
|
|
143
141
|
Returns the endpoint's API response.
|
|
144
142
|
|
|
145
|
-
### `infoBuyResponse(endpoint: string,
|
|
143
|
+
### `infoBuyResponse(endpoint: string, userWalletPrivateKey: string, rpcUrl?: string, opts?: TxResponseOptions & { fastMode?: boolean }): Promise<any>`
|
|
146
144
|
|
|
147
|
-
Combines `info`, `buy`, and `txResponse` into a single call
|
|
145
|
+
Combines `info`, `buy`, and `txResponse` into a single call.
|
|
146
|
+
- `userWalletPrivateKey`: The private key of the wallet sending the funds.
|
|
148
147
|
- `rpcUrl` (Optional): Overrides the default public RPC URL for the payment.
|
|
149
|
-
- `opts` (Optional): Contains `fastMode` boolean and any `TxResponseOptions`.
|
|
148
|
+
- `opts` (Optional): Contains `fastMode` boolean (currently no effect) and any `TxResponseOptions`.
|
|
150
149
|
|
|
151
150
|
Returns the final API response.
|
|
152
151
|
|
|
@@ -155,16 +154,16 @@ Returns the final API response.
|
|
|
155
154
|
```typescript
|
|
156
155
|
// Defined in the SDK
|
|
157
156
|
interface InfoResponse {
|
|
158
|
-
requiredAmount: string; // Amount in
|
|
157
|
+
requiredAmount: string; // Amount in wei (string)
|
|
159
158
|
walletAddress: string;
|
|
160
|
-
httpMethod: string;
|
|
161
|
-
tokenAddress?: string;
|
|
162
|
-
chain: 'eth' | '
|
|
159
|
+
httpMethod: string;
|
|
160
|
+
tokenAddress?: string; // ERC20 address
|
|
161
|
+
chain: 'eth' | 'base';
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
interface TxResponseOptions {
|
|
166
|
-
method?: string;
|
|
167
|
-
data?: any;
|
|
165
|
+
method?: string;
|
|
166
|
+
data?: any;
|
|
168
167
|
}
|
|
169
168
|
```
|
|
170
169
|
|
|
@@ -172,7 +171,6 @@ interface TxResponseOptions {
|
|
|
172
171
|
|
|
173
172
|
- **Ethereum:** `https://rpc.ankr.com/eth`
|
|
174
173
|
- **Base:** `https://mainnet.base.org`
|
|
175
|
-
- **Solana:** `https://api.mainnet-beta.solana.com`
|
|
176
174
|
|
|
177
175
|
You can override these by providing the `rpcUrl` parameter to `buy` or `infoBuyResponse`.
|
|
178
176
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,17 +7,14 @@ interface InfoResponse {
|
|
|
7
7
|
walletAddress: string;
|
|
8
8
|
httpMethod: string;
|
|
9
9
|
tokenAddress?: string;
|
|
10
|
-
chain: 'eth' | '
|
|
10
|
+
chain: 'eth' | 'base';
|
|
11
11
|
}
|
|
12
12
|
declare class ApiNow {
|
|
13
13
|
private handlers;
|
|
14
14
|
info(endpoint: string): Promise<InfoResponse>;
|
|
15
|
-
buy(walletAddress: string, amount: bigint,
|
|
16
|
-
rpcUrl?: string, // Optional RPC URL
|
|
17
|
-
tokenAddress?: string, fastMode?: boolean): Promise<string>;
|
|
15
|
+
buy(walletAddress: string, amount: bigint, userWalletPrivateKey: string, chain: 'eth' | 'base', rpcUrl?: string, tokenAddress?: string, fastMode?: boolean): Promise<string>;
|
|
18
16
|
txResponse(endpoint: string, txHash: string, opts?: TxResponseOptions): Promise<any>;
|
|
19
|
-
infoBuyResponse(endpoint: string,
|
|
20
|
-
opts?: TxResponseOptions & {
|
|
17
|
+
infoBuyResponse(endpoint: string, userWalletPrivateKey: string, rpcUrl?: string, opts?: TxResponseOptions & {
|
|
21
18
|
fastMode?: boolean;
|
|
22
19
|
}): Promise<any>;
|
|
23
20
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { ethers, Wallet, isAddress } from 'ethers';
|
|
2
|
-
import { Connection, PublicKey, Transaction, SystemProgram, Keypair, ComputeBudgetProgram } from '@solana/web3.js';
|
|
3
|
-
import { createTransferInstruction, getAssociatedTokenAddress } from '@solana/spl-token';
|
|
4
|
-
import bs58 from 'bs58';
|
|
5
2
|
// Default RPC URLs
|
|
6
|
-
const DEFAULT_ETH_RPC = 'https://rpc.ankr.com/eth';
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
// --- Helper function for fetch ---
|
|
3
|
+
const DEFAULT_ETH_RPC = 'https://rpc.ankr.com/eth';
|
|
4
|
+
const DEFAULT_BASE_RPC = 'https://mainnet.base.org';
|
|
5
|
+
// --- Helper function for fetch (Keep this) ---
|
|
10
6
|
async function fetchJson(url, options) {
|
|
11
7
|
const response = await fetch(url, options);
|
|
12
8
|
if (!response.ok) {
|
|
@@ -15,7 +11,7 @@ async function fetchJson(url, options) {
|
|
|
15
11
|
}
|
|
16
12
|
return response.json();
|
|
17
13
|
}
|
|
18
|
-
// --- Helper function for RPC calls ---
|
|
14
|
+
// --- Helper function for RPC calls (Keep this) ---
|
|
19
15
|
async function sendJsonRpc(rpcUrl, method, params) {
|
|
20
16
|
const response = await fetch(rpcUrl, {
|
|
21
17
|
method: 'POST',
|
|
@@ -40,53 +36,46 @@ async function sendJsonRpc(rpcUrl, method, params) {
|
|
|
40
36
|
return jsonResponse.result;
|
|
41
37
|
}
|
|
42
38
|
class EthereumHandler {
|
|
43
|
-
async buy(walletAddress, amount,
|
|
44
|
-
|
|
45
|
-
const rpc = rpcUrl || (rpcUrl && rpcUrl.includes('base') ? DEFAULT_BASE_RPC : DEFAULT_ETH_RPC); // Determine default based on presence/content of rpcUrl or use general ETH
|
|
39
|
+
async buy(walletAddress, amount, userWalletPrivateKey, rpcUrl, tokenAddress) {
|
|
40
|
+
const rpc = rpcUrl || (rpcUrl && rpcUrl.includes('base') ? DEFAULT_BASE_RPC : DEFAULT_ETH_RPC);
|
|
46
41
|
if (!walletAddress || !isAddress(walletAddress)) {
|
|
47
42
|
throw new Error('Invalid recipient wallet address');
|
|
48
43
|
}
|
|
49
|
-
const wallet = new Wallet(
|
|
44
|
+
const wallet = new Wallet(userWalletPrivateKey);
|
|
50
45
|
const senderAddress = wallet.address;
|
|
51
46
|
try {
|
|
52
|
-
// Get nonce manually
|
|
53
47
|
const nonce = await sendJsonRpc(rpc, 'eth_getTransactionCount', [senderAddress, 'latest']);
|
|
54
|
-
|
|
55
|
-
const feeData = await sendJsonRpc(rpc, 'eth_gasPrice', []); // Using legacy gasPrice for simplicity, could fetch EIP-1559 fees
|
|
48
|
+
const feeData = await sendJsonRpc(rpc, 'eth_gasPrice', []);
|
|
56
49
|
let txRequest;
|
|
57
50
|
if (tokenAddress) {
|
|
58
51
|
if (!isAddress(tokenAddress)) {
|
|
59
52
|
throw new Error('Invalid token address');
|
|
60
53
|
}
|
|
61
|
-
// ERC20 Transfer
|
|
62
54
|
const abi = ["function transfer(address to, uint256 amount)"];
|
|
63
55
|
const iface = new ethers.Interface(abi);
|
|
64
56
|
const data = iface.encodeFunctionData("transfer", [walletAddress, amount]);
|
|
65
57
|
txRequest = {
|
|
66
58
|
to: tokenAddress,
|
|
67
59
|
nonce: parseInt(nonce, 16),
|
|
68
|
-
gasPrice: feeData,
|
|
69
|
-
|
|
70
|
-
gasLimit: 100000, // Adjust as necessary for ERC20 transfers
|
|
60
|
+
gasPrice: feeData,
|
|
61
|
+
gasLimit: 100000,
|
|
71
62
|
data: data,
|
|
72
|
-
chainId: (await sendJsonRpc(rpc, 'eth_chainId', [])),
|
|
63
|
+
chainId: (await sendJsonRpc(rpc, 'eth_chainId', [])),
|
|
73
64
|
};
|
|
74
65
|
}
|
|
75
66
|
else {
|
|
76
|
-
// Native ETH/Base Transfer
|
|
77
67
|
txRequest = {
|
|
78
68
|
to: walletAddress,
|
|
79
69
|
value: amount,
|
|
80
70
|
nonce: parseInt(nonce, 16),
|
|
81
|
-
gasPrice: feeData,
|
|
82
|
-
gasLimit: 21000,
|
|
83
|
-
chainId: (await sendJsonRpc(rpc, 'eth_chainId', [])),
|
|
71
|
+
gasPrice: feeData,
|
|
72
|
+
gasLimit: 21000,
|
|
73
|
+
chainId: (await sendJsonRpc(rpc, 'eth_chainId', [])),
|
|
84
74
|
};
|
|
85
75
|
}
|
|
86
76
|
const signedTx = await wallet.signTransaction(txRequest);
|
|
87
77
|
const txHash = await sendJsonRpc(rpc, 'eth_sendRawTransaction', [signedTx]);
|
|
88
|
-
|
|
89
|
-
// This would involve polling eth_getTransactionReceipt
|
|
78
|
+
console.log(`Transaction sent: ${txHash}. Confirmation check not implemented.`);
|
|
90
79
|
return txHash;
|
|
91
80
|
}
|
|
92
81
|
catch (error) {
|
|
@@ -95,125 +84,13 @@ class EthereumHandler {
|
|
|
95
84
|
}
|
|
96
85
|
}
|
|
97
86
|
}
|
|
98
|
-
class SolanaHandler {
|
|
99
|
-
// Keep using Connection for some helpers, but sendRawTransaction manually
|
|
100
|
-
async getConnection(rpcUrl) {
|
|
101
|
-
const rpc = rpcUrl || DEFAULT_SOL_RPC;
|
|
102
|
-
return new Connection(rpc, {
|
|
103
|
-
commitment: 'processed',
|
|
104
|
-
confirmTransactionInitialTimeout: 10000 // Used by getMint etc.
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
async buy(walletAddress, amount, pkey, rpcUrl, tokenAddress, fastMode) {
|
|
108
|
-
const rpc = rpcUrl || DEFAULT_SOL_RPC;
|
|
109
|
-
const connection = await this.getConnection(rpc); // Still useful for some reads
|
|
110
|
-
try {
|
|
111
|
-
const recipientPubkey = new PublicKey(walletAddress);
|
|
112
|
-
const senderKeypair = Keypair.fromSecretKey(bs58.decode(pkey));
|
|
113
|
-
const transaction = new Transaction();
|
|
114
|
-
// Add priority fee instruction
|
|
115
|
-
transaction.add(ComputeBudgetProgram.setComputeUnitPrice({
|
|
116
|
-
microLamports: 50000 // Example fee, adjust as needed
|
|
117
|
-
}));
|
|
118
|
-
// Add compute unit limit if necessary, e.g., for token transfers
|
|
119
|
-
transaction.add(ComputeBudgetProgram.setComputeUnitLimit({
|
|
120
|
-
units: tokenAddress ? 200000 : 50000 // Higher limit for token ops
|
|
121
|
-
}));
|
|
122
|
-
if (tokenAddress) {
|
|
123
|
-
const mint = new PublicKey(tokenAddress);
|
|
124
|
-
// Use connection helpers for these reads
|
|
125
|
-
const senderATA = await getAssociatedTokenAddress(mint, senderKeypair.publicKey);
|
|
126
|
-
const recipientATA = await getAssociatedTokenAddress(mint, recipientPubkey);
|
|
127
|
-
// Check if recipient ATA exists, if not, add instruction to create it
|
|
128
|
-
const recipientATAInfo = await connection.getAccountInfo(recipientATA);
|
|
129
|
-
if (!recipientATAInfo) {
|
|
130
|
-
// This part needs @solana/spl-token's createAssociatedTokenAccountInstruction
|
|
131
|
-
// Import: import { createAssociatedTokenAccountInstruction } from '@solana/spl-token';
|
|
132
|
-
// transaction.add(
|
|
133
|
-
// createAssociatedTokenAccountInstruction(
|
|
134
|
-
// senderKeypair.publicKey, // Payer
|
|
135
|
-
// recipientATA,
|
|
136
|
-
// recipientPubkey,
|
|
137
|
-
// mint
|
|
138
|
-
// )
|
|
139
|
-
// );
|
|
140
|
-
console.warn("Recipient Associated Token Account does not exist. Auto-creation commented out. Ensure recipient has the ATA.");
|
|
141
|
-
// For now, we'll proceed assuming it exists or will be created elsewhere.
|
|
142
|
-
// Production code should handle this creation properly.
|
|
143
|
-
}
|
|
144
|
-
transaction.add(createTransferInstruction(senderATA, recipientATA, senderKeypair.publicKey, Number(amount) // SPL transfer amount expects number
|
|
145
|
-
));
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
// SOL transfer
|
|
149
|
-
transaction.add(SystemProgram.transfer({
|
|
150
|
-
fromPubkey: senderKeypair.publicKey,
|
|
151
|
-
toPubkey: recipientPubkey,
|
|
152
|
-
lamports: Number(amount) // SystemProgram.transfer expects number
|
|
153
|
-
}));
|
|
154
|
-
}
|
|
155
|
-
// Get blockhash manually
|
|
156
|
-
const { blockhash, lastValidBlockHeight } = await sendJsonRpc(rpc, 'getLatestBlockhash', [{ 'commitment': 'processed' }]);
|
|
157
|
-
transaction.recentBlockhash = blockhash;
|
|
158
|
-
transaction.feePayer = senderKeypair.publicKey;
|
|
159
|
-
transaction.sign(senderKeypair);
|
|
160
|
-
const rawTx = transaction.serialize();
|
|
161
|
-
const base64Tx = Buffer.from(rawTx).toString('base64');
|
|
162
|
-
// Send transaction manually via RPC
|
|
163
|
-
const signature = await sendJsonRpc(rpc, 'sendTransaction', [
|
|
164
|
-
base64Tx,
|
|
165
|
-
{
|
|
166
|
-
encoding: 'base64',
|
|
167
|
-
skipPreflight: false, // Usually false
|
|
168
|
-
preflightCommitment: 'processed',
|
|
169
|
-
maxRetries: 2
|
|
170
|
-
}
|
|
171
|
-
]);
|
|
172
|
-
// Optionally confirm transaction if not in fastMode
|
|
173
|
-
if (!fastMode) {
|
|
174
|
-
console.log('Waiting for Solana confirmation (up to 30s)... Signature:', signature);
|
|
175
|
-
try {
|
|
176
|
-
// Ensure blockhash is defined before confirming
|
|
177
|
-
if (!transaction.recentBlockhash) {
|
|
178
|
-
throw new Error("Transaction recentBlockhash is missing, cannot confirm.");
|
|
179
|
-
}
|
|
180
|
-
await connection.confirmTransaction({
|
|
181
|
-
signature,
|
|
182
|
-
blockhash: transaction.recentBlockhash, // Now confirmed non-undefined
|
|
183
|
-
lastValidBlockHeight // Use the height we got
|
|
184
|
-
}, 'processed'); // or 'confirmed'/'finalized'
|
|
185
|
-
console.log('Solana transaction confirmed!');
|
|
186
|
-
}
|
|
187
|
-
catch (confirmError) {
|
|
188
|
-
console.error(`Solana confirmation failed for ${signature}:`, confirmError);
|
|
189
|
-
// Don't necessarily throw here, tx might still succeed eventually
|
|
190
|
-
// Consider returning signature but warning about confirmation failure
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return signature;
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
console.error('Detailed SOL error:', error);
|
|
197
|
-
// Try to provide more specific error info if possible
|
|
198
|
-
if (error instanceof Error && error.message.includes('AccountNotFound')) {
|
|
199
|
-
throw new Error(`Solana transaction failed: Source token account might not exist or have funds. ${error.message}`);
|
|
200
|
-
}
|
|
201
|
-
if (error instanceof Error && error.message.includes('Invalid private key')) {
|
|
202
|
-
throw new Error(`Solana transaction failed: Invalid private key provided. ${error.message}`);
|
|
203
|
-
}
|
|
204
|
-
throw new Error(`Solana transaction failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
87
|
class ApiNow {
|
|
209
88
|
constructor() {
|
|
210
89
|
this.handlers = {
|
|
211
90
|
eth: new EthereumHandler(),
|
|
212
|
-
|
|
213
|
-
base: new EthereumHandler() // Base uses Ethereum handler
|
|
91
|
+
base: new EthereumHandler()
|
|
214
92
|
};
|
|
215
93
|
}
|
|
216
|
-
// Use helper function
|
|
217
94
|
async info(endpoint) {
|
|
218
95
|
if (!endpoint || typeof endpoint !== 'string') {
|
|
219
96
|
throw new Error('Invalid endpoint URL');
|
|
@@ -226,10 +103,7 @@ class ApiNow {
|
|
|
226
103
|
throw new Error(`Could not get endpoint info: ${error instanceof Error ? error.message : String(error)}`);
|
|
227
104
|
}
|
|
228
105
|
}
|
|
229
|
-
|
|
230
|
-
async buy(walletAddress, amount, pkey, chain, // Chain is now required to choose handler
|
|
231
|
-
rpcUrl, // Optional RPC URL
|
|
232
|
-
tokenAddress, fastMode) {
|
|
106
|
+
async buy(walletAddress, amount, userWalletPrivateKey, chain, rpcUrl, tokenAddress, fastMode) {
|
|
233
107
|
const handler = this.handlers[chain];
|
|
234
108
|
if (!handler) {
|
|
235
109
|
throw new Error(`Unsupported chain: ${chain}`);
|
|
@@ -237,10 +111,8 @@ class ApiNow {
|
|
|
237
111
|
if (amount <= 0n) {
|
|
238
112
|
throw new Error('Amount must be positive.');
|
|
239
113
|
}
|
|
240
|
-
|
|
241
|
-
return handler.buy(walletAddress, amount, pkey, rpcUrl, tokenAddress, fastMode);
|
|
114
|
+
return handler.buy(walletAddress, amount, userWalletPrivateKey, rpcUrl, tokenAddress);
|
|
242
115
|
}
|
|
243
|
-
// Use helper function
|
|
244
116
|
async txResponse(endpoint, txHash, opts = {}) {
|
|
245
117
|
if (!endpoint || typeof endpoint !== 'string') {
|
|
246
118
|
throw new Error('Invalid endpoint URL');
|
|
@@ -249,12 +121,11 @@ class ApiNow {
|
|
|
249
121
|
throw new Error('Invalid transaction hash');
|
|
250
122
|
}
|
|
251
123
|
const url = new URL(endpoint);
|
|
252
|
-
// Append txHash respecting existing query params
|
|
253
124
|
url.searchParams.append('txHash', txHash);
|
|
254
125
|
try {
|
|
255
126
|
return await fetchJson(url.toString(), {
|
|
256
|
-
method: opts.method || 'GET',
|
|
257
|
-
headers: { 'Content-Type': 'application/json' },
|
|
127
|
+
method: opts.method || 'GET',
|
|
128
|
+
headers: { 'Content-Type': 'application/json' },
|
|
258
129
|
body: opts.data ? JSON.stringify(opts.data) : undefined
|
|
259
130
|
});
|
|
260
131
|
}
|
|
@@ -263,21 +134,16 @@ class ApiNow {
|
|
|
263
134
|
throw new Error(`Could not get transaction response: ${error instanceof Error ? error.message : String(error)}`);
|
|
264
135
|
}
|
|
265
136
|
}
|
|
266
|
-
|
|
267
|
-
async infoBuyResponse(endpoint, pkey, rpcUrl, // Optional RPC URL
|
|
268
|
-
opts = {}) {
|
|
137
|
+
async infoBuyResponse(endpoint, userWalletPrivateKey, rpcUrl, opts = {}) {
|
|
269
138
|
console.log(`Starting infoBuyResponse for endpoint: ${endpoint}`);
|
|
270
|
-
// 1. Get Info
|
|
271
139
|
const info = await this.info(endpoint);
|
|
272
140
|
console.log("Received info:", info);
|
|
273
141
|
const { requiredAmount, walletAddress, chain, tokenAddress } = info;
|
|
274
142
|
if (!chain || !this.handlers[chain]) {
|
|
275
143
|
throw new Error(`Unsupported chain specified by endpoint: ${chain}`);
|
|
276
144
|
}
|
|
277
|
-
// Convert required amount string (assuming decimals handled by endpoint info) to bigint
|
|
278
145
|
let amountBigInt;
|
|
279
146
|
try {
|
|
280
|
-
// Assume requiredAmount is in native smallest units (wei, lamports)
|
|
281
147
|
amountBigInt = BigInt(requiredAmount);
|
|
282
148
|
if (amountBigInt <= 0n) {
|
|
283
149
|
throw new Error('Required amount must be positive.');
|
|
@@ -287,18 +153,13 @@ class ApiNow {
|
|
|
287
153
|
throw new Error(`Invalid requiredAmount format: ${requiredAmount}`);
|
|
288
154
|
}
|
|
289
155
|
console.log(`Attempting payment: Chain=${chain}, To=${walletAddress}, Amount=${amountBigInt.toString()}, Token=${tokenAddress || 'Native'}`);
|
|
290
|
-
|
|
291
|
-
const txHash = await this.buy(walletAddress, amountBigInt, pkey, chain, // Pass the chain from info
|
|
292
|
-
rpcUrl, // Pass optional rpcUrl
|
|
293
|
-
tokenAddress, opts.fastMode);
|
|
156
|
+
const txHash = await this.buy(walletAddress, amountBigInt, userWalletPrivateKey, chain, rpcUrl, tokenAddress, opts.fastMode);
|
|
294
157
|
console.log(`Transaction sent: ${txHash}`);
|
|
295
|
-
// 3. Get Tx Response
|
|
296
|
-
// Add a small delay before fetching the response, especially for non-fast mode
|
|
297
158
|
if (!opts.fastMode) {
|
|
298
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
299
160
|
}
|
|
300
161
|
else {
|
|
301
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
162
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
302
163
|
}
|
|
303
164
|
console.log(`Fetching response for tx: ${txHash}`);
|
|
304
165
|
return this.txResponse(endpoint, txHash, opts);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apinow-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "ApiNow SDK · The endpoint vending machine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,8 +21,6 @@
|
|
|
21
21
|
"author": "ApiNow.fun",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@solana/spl-token": "^0.4.12",
|
|
25
|
-
"@solana/web3.js": "^1.98.0",
|
|
26
24
|
"bs58": "^6.0.0",
|
|
27
25
|
"ethers": "^6.13.5"
|
|
28
26
|
},
|