@hot-pot/hotpot-sdk-ts 0.0.1

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,82 @@
1
+ import { HotPotApiClient } from '../../src/apiClient.js';
2
+ import { preparePermit2Approval } from '../../src/approvals.js';
3
+ import { SwapType } from '../../src/types.js';
4
+
5
+ async function main() {
6
+ // Initialize the HotPot API client with the API key from environment variables
7
+ const apiKey = process.env['API_KEY'];
8
+ if (!apiKey) {
9
+ throw new Error('API_KEY environment variable is not set');
10
+ }
11
+
12
+ const client = new HotPotApiClient('https://api.hotpot.com/affiliate/v1', apiKey);
13
+
14
+ // Example parameters for getting a quote
15
+ const sourceChain = 1; // Ethereum mainnet
16
+ const sourceToken = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // USDC token address
17
+ const destChain = 1; // Ethereum mainnet
18
+ const destToken = '0x2791Bca1f2de4661ED88A3C59D7D5E7383a7f9D7'; // USDC token address on Polygon
19
+ const amount = 1_000_000; // Amount in USD (or base units depending on token)
20
+ const slippageBps = 50n; // 0.5% slippage
21
+ const swapType = SwapType.Optimized;
22
+
23
+ // Get a quote for the swap
24
+ console.log('Getting quote...');
25
+ const quote = await client.getQuote(
26
+ sourceChain,
27
+ sourceToken,
28
+ destChain,
29
+ destToken,
30
+ amount,
31
+ slippageBps,
32
+ swapType,
33
+ );
34
+ console.log('Quote received:', quote);
35
+
36
+ // Example user addresses
37
+ const userSourceAddress = '0xUserSourceAddress';
38
+ const userDestinationAddress = '0xUserDestinationAddress';
39
+ const refundAddress = '0xRefundAddress';
40
+
41
+ // Create an intent based on the quote
42
+ console.log('Creating intent...');
43
+ const intentData = await client.createIntent(
44
+ quote.id,
45
+ null, // userSourcePublicKey (null for EVM)
46
+ userSourceAddress,
47
+ userDestinationAddress,
48
+ refundAddress,
49
+ );
50
+ console.log('Intent created:', intentData);
51
+
52
+ // Prepare the permit2 approval for signing
53
+ const permit2Approval = preparePermit2Approval(
54
+ intentData.params_to_sign as any, // Cast to Permit2ApprovalToSign
55
+ quote,
56
+ userSourceAddress,
57
+ intentData.deadline_secs,
58
+ );
59
+ console.log('Permit2 approval prepared:', permit2Approval);
60
+
61
+ // In a real scenario, you would sign the permit2Approval here using a wallet
62
+ // For example: const signature = await wallet.signTypedData(permit2Approval);
63
+ // For this example, we'll simulate a signed approval
64
+ const signedApproval = {
65
+ type: 'permit2' as const,
66
+ signed_data: '0xSimulatedSignature', // Replace with actual signature in production
67
+ };
68
+
69
+ // Add the approval to the intent
70
+ console.log('Adding approval...');
71
+ await client.addApproval(signedApproval, intentData.intent_id);
72
+ console.log('Approval added');
73
+
74
+ // Get the current status of the intent
75
+ console.log('Getting intent status...');
76
+ const status = await client.getIntentStatus(intentData.intent_id);
77
+ console.log('Intent status:', status);
78
+ }
79
+
80
+ await main()
81
+ .then(() => console.log('Example completed'))
82
+ .catch(console.error);
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "include": ["./"],
4
+ "compilerOptions": {
5
+ "noEmit": true,
6
+ },
7
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@hot-pot/hotpot-sdk-ts",
3
+ "private": false,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist",
8
+ "files": [
9
+ "**"
10
+ ],
11
+ "types": "./dist/index.d.ts",
12
+ "dependencies": {
13
+ "axios": "^1.13.4",
14
+ "viem": "^2.45.0"
15
+ },
16
+ "scripts": {
17
+ "build": "rm -rf dist && tsc",
18
+ "lint": "eslint . --ext .ts",
19
+ "lint:fix": "eslint . --ext .ts --fix",
20
+ "prettier": "prettier --ignore-path .prettierignore --write \"src/**/*.ts\""
21
+ },
22
+ "devDependencies": {
23
+ "@eslint/js": "9.25.1",
24
+ "@types/bun": "latest",
25
+ "@types/node": "22.15.16",
26
+ "@types/react": "19.0.10",
27
+ "@types/react-dom": "19.0.4",
28
+ "@typescript-eslint/eslint-plugin": "8.31.0",
29
+ "@typescript-eslint/parser": "8.31.0",
30
+ "@vitejs/plugin-react": "4.3.4",
31
+ "eslint": "9.25.1",
32
+ "eslint-plugin-prettier": "5.2.6",
33
+ "eslint-plugin-simple-import-sort": "12.1.1",
34
+ "eslint-plugin-unicorn": "58.0.0",
35
+ "eslint-plugin-unused-imports": "4.1.4",
36
+ "typescript-eslint": "8.26.1",
37
+ "jiti": "2.4.2"
38
+ },
39
+ "peerDependencies": {
40
+ "typescript": "^5.0.0"
41
+ }
42
+ }
@@ -0,0 +1,208 @@
1
+ import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
2
+ import axios from 'axios';
3
+
4
+ import type {
5
+ CreateIntentData,
6
+ IntentStatus,
7
+ Network,
8
+ PaginatedResponse,
9
+ PublicSwapWithAdditionalInfo,
10
+ Quote,
11
+ SignedApproval,
12
+ SwapType,
13
+ Token,
14
+ } from './types';
15
+
16
+ export class HotPotApiClient {
17
+ private client: AxiosInstance;
18
+
19
+ constructor(
20
+ apiUrl: string,
21
+ apiKey: string,
22
+ axiosInstance?: AxiosInstance,
23
+ config?: AxiosRequestConfig,
24
+ ) {
25
+ this.client =
26
+ axiosInstance ||
27
+ axios.create({
28
+ baseURL: apiUrl,
29
+ headers: {
30
+ 'x-api-key': apiKey,
31
+ },
32
+ ...config,
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Fetches the best quote for a swap based on the given parameters
38
+ * @param sourceChain The source chain ID
39
+ * @param sourceToken The source token address
40
+ * @param destChain The destination chain ID
41
+ * @param destToken The destination token address
42
+ * @param amount The amount to swap
43
+ * @param slippageBps The slippage in basis points
44
+ * @param swapType The type of swap
45
+ * @param retailUserId Optional retail user ID
46
+ * @returns A promise that resolves to the Quote object
47
+ */
48
+ async getQuote(
49
+ sourceChain: number,
50
+ sourceToken: string,
51
+ destChain: number,
52
+ destToken: string,
53
+ amount: number,
54
+ slippageBps: bigint,
55
+ swapType: SwapType,
56
+ retailUserId: string | null = null,
57
+ ): Promise<Quote> {
58
+ const response: AxiosResponse<Quote> = await this.client.post('/quotes/best', {
59
+ source_chain: sourceChain,
60
+ source_token: sourceToken,
61
+ dest_chain: destChain,
62
+ dest_token: destToken,
63
+ amount,
64
+ slippage_bps: slippageBps.toString(),
65
+ swap_type: swapType,
66
+ retail_user_id: retailUserId,
67
+ });
68
+
69
+ return response.data;
70
+ }
71
+
72
+ /**
73
+ * Creates a new swap intent with the provided details
74
+ * @param quoteId The quote ID to use for the intent
75
+ * @param userSourcePublicKey The user's source public key
76
+ * @param userSourceAddress The user's source address
77
+ * @param userDestinationAddress The user's destination address
78
+ * @param refundAddress The refund address
79
+ * @returns A promise that resolves to the CreateIntentData object
80
+ */
81
+ async createIntent(
82
+ quoteId: string,
83
+ userSourcePublicKey: string | null,
84
+ userSourceAddress: string,
85
+ userDestinationAddress: string,
86
+ refundAddress: string,
87
+ ): Promise<CreateIntentData> {
88
+ const response: AxiosResponse<CreateIntentData> = await this.client.post('/intents', {
89
+ quote_id: quoteId,
90
+ user_source_public_key: userSourcePublicKey,
91
+ user_source_address: userSourceAddress,
92
+ user_destination_address: userDestinationAddress,
93
+ refund_address: refundAddress,
94
+ });
95
+
96
+ return response.data;
97
+ }
98
+
99
+ /**
100
+ * Adds an approval signature for a specific intent
101
+ * @param signedData The signed approval data
102
+ * @param intentId The intent ID to add the approval to
103
+ */
104
+ async addApproval(signedData: SignedApproval, intentId: string): Promise<void> {
105
+ await this.client.post(`/intents/${intentId}/approvals`, signedData);
106
+ }
107
+
108
+ /**
109
+ * Retrieves details of a specific intent
110
+ * @param intentId The intent ID to retrieve
111
+ * @returns A promise that resolves to the PublicSwapWithAdditionalInfo object
112
+ */
113
+ async getIntent(intentId: string): Promise<PublicSwapWithAdditionalInfo> {
114
+ const response: AxiosResponse<PublicSwapWithAdditionalInfo> = await this.client.get(
115
+ `/intents/${intentId}`,
116
+ );
117
+
118
+ return response.data;
119
+ }
120
+
121
+ /**
122
+ * Gets the current status of a specific intent
123
+ * @param intentId The intent ID to check
124
+ * @returns A promise that resolves to the IntentStatus
125
+ */
126
+ async getIntentStatus(intentId: string): Promise<IntentStatus> {
127
+ const response: AxiosResponse<{ status: IntentStatus }> = await this.client.get(
128
+ `/intents/${intentId}/status`,
129
+ );
130
+
131
+ return response.data.status;
132
+ }
133
+
134
+ /**
135
+ * Lists the swap history for a retail user or wallet address
136
+ * @param walletAddressOrRetailId The wallet address or retail ID
137
+ * @param limit The maximum number of results to return (default: 20)
138
+ * @param offset The offset for pagination (default: 0)
139
+ * @param active Whether to show only active swaps (default: false)
140
+ * @returns A promise that resolves to a paginated response of PublicSwapWithAdditionalInfo objects
141
+ */
142
+ async listRetailUserHistory(
143
+ walletAddressOrRetailId: string,
144
+ limit: number = 20,
145
+ offset: number = 0,
146
+ active: boolean = false,
147
+ ): Promise<PaginatedResponse<PublicSwapWithAdditionalInfo>> {
148
+ const response: AxiosResponse<PaginatedResponse<PublicSwapWithAdditionalInfo>> =
149
+ await this.client.get(
150
+ `/swaps/history/${walletAddressOrRetailId}?limit=${limit}&offset=${offset}&active=${active}`,
151
+ );
152
+
153
+ return response.data;
154
+ }
155
+
156
+ /**
157
+ * Retrieves swap details by intent ID
158
+ * @param intentId The intent ID to retrieve swap details for
159
+ * @returns A promise that resolves to the PublicSwapWithAdditionalInfo object
160
+ */
161
+ async getSwapByIntentId(intentId: string): Promise<PublicSwapWithAdditionalInfo> {
162
+ const response: AxiosResponse<PublicSwapWithAdditionalInfo> = await this.client.get(
163
+ `/swaps/intents/${intentId}`,
164
+ );
165
+
166
+ return response.data;
167
+ }
168
+
169
+ /**
170
+ * Lists available networks, optionally filtered by token
171
+ * @param token Optional token to filter networks by
172
+ * @returns A promise that resolves to an array of Network objects
173
+ */
174
+ async listNetworks(token?: string): Promise<Network[]> {
175
+ const endpoint = token ? `/networks?token=${token}` : '/networks';
176
+ const response: AxiosResponse<Network[]> = await this.client.get(endpoint);
177
+
178
+ return response.data;
179
+ }
180
+
181
+ /**
182
+ * Lists available tokens with pagination and optional filtering
183
+ * @param limit The maximum number of results to return (default: 20)
184
+ * @param offset The offset for pagination (default: 0)
185
+ * @param q Optional search query
186
+ * @param networkId Optional network ID to filter by
187
+ * @returns A promise that resolves to a paginated response of Token objects
188
+ */
189
+ async listTokens(
190
+ limit: number = 20,
191
+ offset: number = 0,
192
+ q?: string,
193
+ networkId?: number,
194
+ ): Promise<PaginatedResponse<Token>> {
195
+ let endpoint = `/tokens?limit=${limit}&offset=${offset}`;
196
+ if (q) {
197
+ endpoint += `&q=${q}`;
198
+ }
199
+ if (networkId) {
200
+ endpoint += `&network_id=${networkId}`;
201
+ }
202
+ const response: AxiosResponse<PaginatedResponse<Token>> = await this.client.get(endpoint);
203
+
204
+ return response.data;
205
+ }
206
+ }
207
+
208
+ export default HotPotApiClient;
@@ -0,0 +1,57 @@
1
+ import { type Permit2ApprovalToSign, type Quote } from './types.js';
2
+
3
+ export interface SignTypedDataParams {
4
+ domain: {
5
+ name: string;
6
+ version?: string;
7
+ chainId: number;
8
+ verifyingContract: `0x${string}`;
9
+ };
10
+ types: Record<string, Array<{ name: string; type: string }>>;
11
+ primaryType: string;
12
+ message: Record<string, any>;
13
+ account: string;
14
+ }
15
+
16
+ /**
17
+ * Prepares a permit2 approval for signing via EIP-712.
18
+ * @param permit2Approval The approval which has to be signed
19
+ * @param quote The quote on which the intent is based
20
+ * @param userAddress Address of the user creating the intent
21
+ * @param deadline The deadline in which the swap must be fulfilled
22
+ */
23
+ export const preparePermit2Approval = (
24
+ permit2Approval: Permit2ApprovalToSign,
25
+ quote: Quote,
26
+ userAddress: string,
27
+ deadline: number,
28
+ ): SignTypedDataParams => {
29
+ const permit2Data = permit2Approval.additional_data;
30
+ const domain = {
31
+ name: permit2Data.domain.name,
32
+ version: permit2Data.domain.version ?? undefined,
33
+ chainId: permit2Data.domain.chain_id,
34
+ verifyingContract: permit2Data.domain.verifying_contract,
35
+ };
36
+
37
+ const witness = permit2Data.witness;
38
+
39
+ const message = {
40
+ permitted: {
41
+ token: quote.source_token,
42
+ amount: BigInt(quote.source_amount_lots),
43
+ },
44
+ spender: permit2Approval.escrow_contract_address,
45
+ nonce: permit2Approval.nonce,
46
+ deadline,
47
+ witness,
48
+ };
49
+
50
+ return {
51
+ domain,
52
+ types: permit2Data.types,
53
+ primaryType: 'PermitWitnessTransferFrom',
54
+ message,
55
+ account: userAddress,
56
+ };
57
+ };
package/src/errors.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { ApiEntity } from './types.js';
2
+
3
+ export interface ApiError {
4
+ code: number;
5
+ entity: ApiEntity;
6
+ message: string;
7
+ }
8
+
9
+ export const API_ERROR_CODES = {
10
+ INSUFFICIENT_ALLOWANCE: 25,
11
+ } as const;
12
+
13
+ export class InsufficientAllowanceError extends Error {
14
+ code: number;
15
+ entity: string;
16
+
17
+ constructor(apiError: ApiError) {
18
+ super(apiError.message);
19
+ this.name = 'InsufficientAllowanceError';
20
+ this.code = apiError.code;
21
+ this.entity = apiError.entity;
22
+ }
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './apiClient.js';
2
+ export * from './approvals.js';
3
+ export * from './errors.js';
4
+ export * from './types.js';
package/src/types.ts ADDED
@@ -0,0 +1,176 @@
1
+ export enum SwapType {
2
+ Standard = 'standard',
3
+ Optimized = 'optimized',
4
+ }
5
+
6
+ export enum IntentStatus {
7
+ Initiated = 'Initiated',
8
+ ApprovalAdded = 'ApprovalAdded',
9
+ Accepted = 'Accepted',
10
+ Declined = 'Declined',
11
+ DeclinedDueToKytCheck = 'DeclinedDueToKytCheck',
12
+ UserDeposited = 'UserDeposited',
13
+ KycRequested = 'KycRequested',
14
+ Fulfilled = 'Fulfilled',
15
+ Expired = 'Expired',
16
+ RefundRequested = 'RefundRequested',
17
+ Refunded = 'Refunded',
18
+ }
19
+
20
+ export enum NetworkType {
21
+ EVM = 'EVM',
22
+ TRON = 'TRON',
23
+ BITCOIN = 'BITCOIN',
24
+ SOLANA = 'SOLANA',
25
+ }
26
+
27
+ export interface Quote {
28
+ id: string;
29
+ resolver_id: string;
30
+ source_chain: number;
31
+ source_token: string;
32
+ dest_chain: number;
33
+ dest_token: string;
34
+ intermediate_token: string | null;
35
+ intermediate_token_amount_min: string | null;
36
+ intermediate_token_amount_max: string | null;
37
+ intermediate_token_decimals: number | null;
38
+ source_amount_lots: string;
39
+ source_amount_decimals: number;
40
+ min_dest_amount_lots: string;
41
+ max_dest_amount_lots: string;
42
+ dest_amount_decimals: number;
43
+ slippage_bps: string;
44
+ expiry: number;
45
+ swap_type: SwapType;
46
+ }
47
+
48
+ export interface CreateIntentData {
49
+ intent_id: string;
50
+ deadline_secs: number;
51
+ secret_hash: `0x${string}`;
52
+ approval_mechanism: 'permit2' | 'htlc' | 'cosign';
53
+ params_to_sign: Permit2ApprovalToSign | HtlcApprovalToSign | CosignApprovalToSign;
54
+ }
55
+
56
+ export type ApprovalType = 'permit2' | 'psbt' | 'cosign';
57
+
58
+ export interface CosignData {
59
+ transaction: string;
60
+ user_address: string;
61
+ }
62
+
63
+ export interface SignedApproval {
64
+ type: ApprovalType;
65
+ signed_data: string | Record<string, string>;
66
+ }
67
+
68
+ export interface Permit2ApprovalToSign {
69
+ escrow_contract_address: string;
70
+ permit2_contract_address: string;
71
+ resolver_deposit_address: string;
72
+ nonce: number;
73
+ additional_data: {
74
+ domain: {
75
+ name: string;
76
+ version: string | null;
77
+ chain_id: number;
78
+ verifying_contract: `0x${string}`;
79
+ };
80
+ types: Record<string, Array<{ name: string; type: string }>>;
81
+ witness: Record<string, unknown>;
82
+ witness_type_string: string;
83
+ witness_hash: `0x${string}`;
84
+ };
85
+ }
86
+
87
+ export interface HtlcApprovalToSign {
88
+ psbt: string;
89
+ inputs: number[];
90
+ }
91
+
92
+ export interface CosignApprovalToSign {
93
+ transaction: string;
94
+ }
95
+
96
+ export interface IntentStatusResponse {
97
+ status: IntentStatus;
98
+ }
99
+
100
+ export interface PublicSwapWithAdditionalInfo {
101
+ intent_id: string;
102
+ status: IntentStatus;
103
+ kyc_link: string | null;
104
+ swap_metadata: SwapMetadata | null;
105
+ source_amount_decimals: number;
106
+ source_amount_lots: string;
107
+ source_chain: number;
108
+ source_token: string;
109
+ dest_amount_decimals: number;
110
+ dest_chain: number;
111
+ dest_token: string;
112
+ min_dest_amount_lots: string;
113
+ max_dest_amount_lots: string;
114
+ }
115
+
116
+ export interface SwapMetadata {
117
+ id: string;
118
+ proxy_address: string;
119
+ user_deposit_tx: string | null;
120
+ fulfill_tx: string | null;
121
+ swap_tx: string | null;
122
+ created_at: Date;
123
+ user_deposited_at: Date | null;
124
+ kyc_requested_at: Date | null;
125
+ fulfilled_at: Date | null;
126
+ withdrawn_at: Date | null;
127
+ swapped_at: Date | null;
128
+ refunded_at: Date | null;
129
+ }
130
+
131
+ export interface PaginatedResponse<T> {
132
+ pagination: {
133
+ total: number;
134
+ limit: number;
135
+ offset: number;
136
+ pages: number;
137
+ };
138
+ data: T[];
139
+ }
140
+
141
+ export interface Network {
142
+ id: number;
143
+ name: string;
144
+ type: NetworkType;
145
+ supports_optimized_swap: boolean;
146
+ icon_url: string;
147
+ supports_custom_tokens: boolean;
148
+ }
149
+
150
+ export interface Token {
151
+ network_id: number;
152
+ name: string;
153
+ contract_address: string;
154
+ symbol: string;
155
+ icon_url: string;
156
+ wrapped_token_address: string | null;
157
+ }
158
+
159
+ export type ApiEntity =
160
+ | 'Approval'
161
+ | 'Intent'
162
+ | 'Network'
163
+ | 'Quote'
164
+ | 'Resolver'
165
+ | 'SupportedToken'
166
+ | 'Swap'
167
+ | 'Token'
168
+ | 'UserNonce'
169
+ | 'Transaction'
170
+ | 'TransactionReceipt'
171
+ | 'InternalEvent'
172
+ | 'BlockchainEvent'
173
+ | 'WatcherRequest'
174
+ | 'Webhooks'
175
+ | 'Affiliate'
176
+ | 'Other';
package/tsconfig.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Enable latest features
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "ESNext",
6
+ "module": "ESNext",
7
+ "moduleDetection": "force",
8
+ "allowJs": true,
9
+ "outDir": "./dist",
10
+
11
+ "baseUrl": "./src",
12
+
13
+ // Bundler mode
14
+ "moduleResolution": "bundler",
15
+ "verbatimModuleSyntax": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+
22
+ // Some stricter flags (disabled by default)
23
+ "noUnusedLocals": false,
24
+ "noUnusedParameters": false,
25
+ "noPropertyAccessFromIndexSignature": false,
26
+
27
+ // declarations stuff
28
+ "declaration": true,
29
+ "declarationDir": "./dist",
30
+ },
31
+ "include": ["src"],
32
+ }