@lombard.finance/sdk 3.6.8 → 3.6.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lombard.finance/sdk",
3
- "version": "3.6.8",
3
+ "version": "3.6.10",
4
4
  "dependencies": {
5
5
  "@lombard.finance/sdk-common": "^3.0.0",
6
6
  "viem": "^2.23.15",
@@ -25,6 +25,7 @@
25
25
  "storybook": "^8.2.9",
26
26
  "typedoc": "^0.26.6",
27
27
  "typescript": "^5.4.5",
28
+ "vitest": "^3.2.4",
28
29
  "wagmi": "^2.14.15"
29
30
  },
30
31
  "engines": {
@@ -39,10 +40,7 @@
39
40
  "./clients/*": "./src/clients/*.ts",
40
41
  "./utils/*": "./src/utils/*.ts"
41
42
  },
42
- "files": [
43
- "dist",
44
- "src"
45
- ],
43
+ "files": ["dist", "src"],
46
44
  "license": "MIT",
47
45
  "peerDependencies": {
48
46
  "@layerzerolabs/lz-v2-utilities": "3.0.17",
@@ -57,9 +55,10 @@
57
55
  "format": "biome format",
58
56
  "lint": "biome lint --diagnostic-level=error",
59
57
  "storybook": "storybook dev -p 6006",
58
+ "test": "vitest run",
60
59
  "types": "tsc --noEmit",
61
60
  "watch": "vite build --watch"
62
61
  },
63
62
  "type": "module",
64
63
  "types": "./src/index.ts"
65
- }
64
+ }
@@ -8,10 +8,8 @@ import { functionType } from '../../stories/components/decorators';
8
8
  import { EXAMPLE_EVM_ADDRESS } from '../../stories/constants';
9
9
  import useQuery from '../../stories/hooks/useQuery';
10
10
  import { Token } from '../../tokens/token-addresses';
11
- import {
12
- IGetDepositBtcAddressParameters,
13
- getDepositBtcAddress,
14
- } from './getDepositBtcAddress';
11
+ import { getDepositBtcAddress } from './getDepositBtcAddress';
12
+ import { IGetDepositBtcAddressParameters } from './types';
15
13
 
16
14
  const meta = {
17
15
  title: 'api/getDepositBtcAddress',
@@ -0,0 +1,264 @@
1
+ import { describe, expect, it, type Mock, vi } from 'vitest';
2
+ vi.mock('./make-request', async () => {
3
+ return {
4
+ makeRequest: vi.fn(async () => undefined),
5
+ };
6
+ });
7
+
8
+ import { getDepositBtcAddress } from './getDepositBtcAddress';
9
+ import { ChainId } from '../../common/chains';
10
+ import { Env } from '@lombard.finance/sdk-common';
11
+ import { getChainNameById } from '../../common/blockchain-identifier';
12
+ import { Token, TOKEN_ADDRESSES } from '../../tokens/token-addresses';
13
+ import { DAY, now, toUnix } from '../../utils/time';
14
+ import { makeRequest } from './make-request';
15
+ import { IDepositAddress, IGetDepositBtcAddressesParameters } from './types';
16
+
17
+ const ACCOUNT_ADDRESS_A = '0x1111111111111111111111111111111111111111';
18
+
19
+ const DEPOSIT_ADDRESS_A = 'bc1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
20
+ const DEPOSIT_ADDRESS_B = 'bc1bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
21
+ const DEPOSIT_ADDRESS_C = 'bc1ccccccccccccccccccccccccccccccccccccccc';
22
+
23
+ const makeDepositAddress = (
24
+ depositAddress: string,
25
+ chainId: ChainId,
26
+ toAddress: string,
27
+ createdAt: number,
28
+ token?: Token,
29
+ ) =>
30
+ ({
31
+ btc_address: depositAddress,
32
+ type: 'ADDRESS_TYPE_DEPOSIT',
33
+ deposit_metadata: {
34
+ to_address: toAddress,
35
+ to_blockchain: getChainNameById(chainId),
36
+ referral: 'lombard',
37
+ ...(token != null
38
+ ? {
39
+ token_address: TOKEN_ADDRESSES[token]?.[Env.prod]?.[chainId],
40
+ aux_version: 1,
41
+ }
42
+ : {}),
43
+ },
44
+ created_at: createdAt.toString(),
45
+ }) as IDepositAddress;
46
+
47
+ describe('getDepositBtcAddress', () => {
48
+ it.each([
49
+ [
50
+ 'Wants LBTC on Ethereum, should return the most recent LBTC address',
51
+ [
52
+ makeDepositAddress(
53
+ DEPOSIT_ADDRESS_A,
54
+ ChainId.ethereum,
55
+ ACCOUNT_ADDRESS_A,
56
+ toUnix(now() - DAY * 2),
57
+ Token.LBTC,
58
+ ),
59
+ makeDepositAddress(
60
+ DEPOSIT_ADDRESS_B,
61
+ ChainId.ethereum,
62
+ ACCOUNT_ADDRESS_A,
63
+ toUnix(now() - DAY * 1),
64
+ Token.LBTC,
65
+ ),
66
+ ],
67
+ Token.LBTC,
68
+ ChainId.ethereum,
69
+ DEPOSIT_ADDRESS_B,
70
+ ],
71
+
72
+ [
73
+ 'Wants LBTC on Ethereum, no token information in the API response, should return the most recent LBTC address',
74
+ [
75
+ makeDepositAddress(
76
+ DEPOSIT_ADDRESS_A,
77
+ ChainId.ethereum,
78
+ ACCOUNT_ADDRESS_A,
79
+ toUnix(now() - DAY * 2),
80
+ ),
81
+ makeDepositAddress(
82
+ DEPOSIT_ADDRESS_B,
83
+ ChainId.ethereum,
84
+ ACCOUNT_ADDRESS_A,
85
+ toUnix(now() - DAY * 1),
86
+ ),
87
+ ],
88
+ Token.LBTC,
89
+ ChainId.ethereum,
90
+ DEPOSIT_ADDRESS_B,
91
+ ],
92
+
93
+ [
94
+ 'Wants LBTC on Katana, only NativeLBTC address, should throw an error',
95
+ [
96
+ makeDepositAddress(
97
+ DEPOSIT_ADDRESS_A,
98
+ ChainId.katana,
99
+ ACCOUNT_ADDRESS_A,
100
+ toUnix(now() - DAY * 2),
101
+ Token.NativeLBTC,
102
+ ),
103
+ ],
104
+ Token.LBTC,
105
+ ChainId.katana,
106
+ undefined,
107
+ ],
108
+
109
+ [
110
+ 'Wants NativeLBTC on Katana, should return the most recent NativeLBTC address',
111
+ [
112
+ makeDepositAddress(
113
+ DEPOSIT_ADDRESS_A,
114
+ ChainId.katana,
115
+ ACCOUNT_ADDRESS_A,
116
+ toUnix(now() - DAY * 1),
117
+ Token.LBTC,
118
+ ),
119
+ makeDepositAddress(
120
+ DEPOSIT_ADDRESS_B,
121
+ ChainId.katana,
122
+ ACCOUNT_ADDRESS_A,
123
+ toUnix(now() - DAY * 2),
124
+ Token.LBTC,
125
+ ),
126
+ makeDepositAddress(
127
+ DEPOSIT_ADDRESS_C,
128
+ ChainId.katana,
129
+ ACCOUNT_ADDRESS_A,
130
+ toUnix(now() - DAY * 2),
131
+ Token.NativeLBTC,
132
+ ),
133
+ ],
134
+ Token.NativeLBTC,
135
+ ChainId.katana,
136
+ DEPOSIT_ADDRESS_C,
137
+ ],
138
+
139
+ [
140
+ 'Wants BTCK (alias of NativeLBTC) on Katana, should return the most recent NativeLBTC address',
141
+ [
142
+ makeDepositAddress(
143
+ DEPOSIT_ADDRESS_A,
144
+ ChainId.katana,
145
+ ACCOUNT_ADDRESS_A,
146
+ toUnix(now() - DAY * 2),
147
+ Token.NativeLBTC,
148
+ ),
149
+ makeDepositAddress(
150
+ DEPOSIT_ADDRESS_B,
151
+ ChainId.katana,
152
+ ACCOUNT_ADDRESS_A,
153
+ toUnix(now() - DAY * 0),
154
+ Token.LBTC,
155
+ ),
156
+ ],
157
+ Token.BTCK,
158
+ ChainId.katana,
159
+ DEPOSIT_ADDRESS_A,
160
+ ],
161
+
162
+ [
163
+ 'Wants NativeLBTC on Katana, no token information in the API response, should throw an error',
164
+ [
165
+ makeDepositAddress(
166
+ DEPOSIT_ADDRESS_A,
167
+ ChainId.katana,
168
+ ACCOUNT_ADDRESS_A,
169
+ toUnix(now() - DAY * 2),
170
+ ),
171
+ makeDepositAddress(
172
+ DEPOSIT_ADDRESS_B,
173
+ ChainId.katana,
174
+ ACCOUNT_ADDRESS_A,
175
+ toUnix(now() - DAY * 0),
176
+ ),
177
+ ],
178
+ Token.BTCK,
179
+ ChainId.katana,
180
+ undefined,
181
+ ],
182
+
183
+ [
184
+ 'Wants LBTC on Ethereum, should return the most recent LBTC address (legacy call)',
185
+ [
186
+ makeDepositAddress(
187
+ DEPOSIT_ADDRESS_A,
188
+ ChainId.ethereum,
189
+ ACCOUNT_ADDRESS_A,
190
+ toUnix(now() - DAY * 2),
191
+ ),
192
+ makeDepositAddress(
193
+ DEPOSIT_ADDRESS_B,
194
+ ChainId.katana,
195
+ ACCOUNT_ADDRESS_A,
196
+ toUnix(now() - DAY * 0),
197
+ ),
198
+ makeDepositAddress(
199
+ DEPOSIT_ADDRESS_C,
200
+ ChainId.base,
201
+ ACCOUNT_ADDRESS_A,
202
+ toUnix(now() - DAY * 0),
203
+ Token.LBTC,
204
+ ),
205
+ makeDepositAddress(
206
+ DEPOSIT_ADDRESS_C,
207
+ ChainId.base,
208
+ ACCOUNT_ADDRESS_A,
209
+ toUnix(now() - DAY * 0),
210
+ Token.NativeLBTC,
211
+ ),
212
+ ],
213
+ undefined,
214
+ ChainId.ethereum,
215
+ DEPOSIT_ADDRESS_A,
216
+ ],
217
+
218
+ [
219
+ 'Wants NativeLBTC on Ethereum, no token address for NativeLBTC on Ethereum, should throw an error.',
220
+ [
221
+ makeDepositAddress(
222
+ DEPOSIT_ADDRESS_A,
223
+ ChainId.ethereum,
224
+ ACCOUNT_ADDRESS_A,
225
+ toUnix(now() - DAY * 2),
226
+ ),
227
+ ],
228
+ Token.NativeLBTC,
229
+ ChainId.ethereum,
230
+ undefined,
231
+ ],
232
+ ])(
233
+ '$0',
234
+ async (
235
+ _description: string,
236
+ API_RESPONSE: IDepositAddress[],
237
+ token: Token | undefined,
238
+ chainId: ChainId,
239
+ expectedAddress: string | undefined,
240
+ ) => {
241
+ const params: IGetDepositBtcAddressesParameters = {
242
+ address: ACCOUNT_ADDRESS_A,
243
+ chainId,
244
+ env: Env.prod,
245
+ partnerId: 'lombard',
246
+ };
247
+
248
+ (makeRequest as Mock).mockImplementation(async () => API_RESPONSE);
249
+
250
+ if (!expectedAddress) {
251
+ await expect(
252
+ getDepositBtcAddress({ ...params, token }),
253
+ ).rejects.toThrow();
254
+ } else {
255
+ const address = await getDepositBtcAddress({
256
+ ...params,
257
+ token,
258
+ });
259
+
260
+ expect(address).toBe(expectedAddress);
261
+ }
262
+ },
263
+ );
264
+ });
@@ -1,168 +1,20 @@
1
- import axios from 'axios';
2
- import { getApiConfig } from '../../common/api-config';
1
+ import { isSolanaChain, isSuiChain, isValidChain } from '../../common/chains';
3
2
  import {
4
- BlockchainIdentifier,
5
- getChainNameById,
6
- } from '../../common/blockchain-identifier';
7
- import {
8
- ChainId,
9
- isValidChain,
10
- SolanaChain,
11
- SuiChain,
12
- } from '../../common/chains';
13
- import { IEnvParam } from '../../common/parameters';
14
- import { Token } from '../../tokens/token-addresses';
3
+ getSolanaTokenAddress,
4
+ getSuiTokenAddress,
5
+ Token,
6
+ } from '../../tokens/token-addresses';
15
7
  import { getTokenContractInfo } from '../../tokens/tokens';
16
-
17
- export interface IDepositAddress {
18
- /**
19
- * The deposit address for BTC.
20
- */
21
- btc_address: string;
22
- /**
23
- * The address creation timestamp.
24
- */
25
- created_at: string;
26
- /**
27
- * A flag determining whether an address is deprecated (no longer valid for depositing BTC).
28
- */
29
- deprecated?: boolean;
30
- /**
31
- * Type of an address.
32
- * @constant {string} - ADDRESS_TYPE_DEPOSIT
33
- */
34
- type: string;
35
- /**
36
- * A flag determining whether an address has been used.
37
- */
38
- used?: boolean;
39
-
40
- /**
41
- * The deposit address metadata
42
- */
43
- deposit_metadata: {
44
- /**
45
- * The partner (referral) id.
46
- */
47
- referral: string;
48
- /**
49
- * The partner (referral) id.
50
- */
51
- partner_id: string;
52
- /**
53
- * The destination address.
54
- */
55
- to_address: string;
56
- /**
57
- * The destination blockchain corresponding to the `to_address`
58
- */
59
- to_blockchain: BlockchainIdentifier;
60
- /**
61
- * The destination token address
62
- */
63
- token_address?: string;
64
- /**
65
- * The aux version (paired with the token_address)
66
- */
67
- aux_version?: number;
68
- };
69
- }
70
-
71
- interface IDepositAddressesResponse {
72
- addresses: IDepositAddress[];
73
- has_more?: boolean;
74
- }
75
-
76
- interface IApiError {
77
- code: number;
78
- message?: string;
79
- }
80
-
81
- export interface IGetDepositBtcAddressesParameters extends IEnvParam {
82
- /**
83
- * The destination address where LBTC will be claimed.
84
- */
85
- address: string;
86
- /**
87
- * The destination chain where the `address` exists and where LBTC will be claimed.
88
- */
89
- chainId: ChainId | SuiChain | SolanaChain;
90
- /**
91
- * The maximum number of items to return.
92
- * @default {number} 1
93
- */
94
- limit?: number;
95
-
96
- /**
97
- * The number of items to skip before starting to return the items.
98
- * @default {number} 0
99
- */
100
- offset?: number;
101
-
102
- /**
103
- * The partner (referral) id.
104
- * @default {string} "lombard"
105
- */
106
- partnerId?: string;
107
- }
108
-
109
- async function makeRequest({
110
- address,
111
- chainId,
112
- env,
113
- limit,
114
- offset,
115
- partnerId,
116
- }: IGetDepositBtcAddressesParameters) {
117
- const { baseApiUrl } = getApiConfig(env);
118
-
119
- // throws an error if `chainId` is unknown
120
- const destinationBlockchain = getChainNameById(chainId);
121
-
122
- const params = {
123
- asc: false,
124
- limit,
125
- offset,
126
- referralId: partnerId || 'lombard',
127
- };
128
-
129
- // remove undefined fields, undefined limit and offset params cause error
130
- for (const [k, v] of Object.entries(params)) {
131
- if (v === undefined) {
132
- delete params[k as keyof typeof params];
133
- }
134
- }
135
-
136
- const url = `api/v1/address/destination/${destinationBlockchain}/${address}`;
137
- try {
138
- const { data } = await axios.get<IDepositAddressesResponse>(url, {
139
- baseURL: baseApiUrl,
140
- params,
141
- });
142
-
143
- return data.addresses || [];
144
- } catch (err) {
145
- if (axios.isAxiosError<IApiError>(err)) {
146
- const message = err.response?.data.message;
147
- throw new Error(message);
148
- }
149
- }
150
- }
151
-
152
- export type IGetDepositBtcAddressParameters = { token?: Token } & Pick<
8
+ import type {
153
9
  IGetDepositBtcAddressesParameters,
154
- 'address' | 'chainId' | 'env' | 'partnerId'
155
- >;
10
+ IGetDepositBtcAddressParameters,
11
+ } from './types';
12
+ import { makeRequest } from './make-request';
13
+ import { getChainNameById } from '../../common/blockchain-identifier';
156
14
 
157
15
  /**
158
16
  * Returns the current address for depositing BTC by given parameters.
159
17
  *
160
- * @param {IGetDepositBtcAddressParameters} parameters - The parameters.
161
- * @param {string} parameters.address - The account address.
162
- * @param {ChainId} parameters.chainId - The chain id.
163
- * @param {Env} parameters.env - The optional environment identifier.
164
- * @param {string} parameters.partnerId - The partner (referral) id.
165
- *
166
18
  * @throws {Error} - Throws an error if no address found or the provided chain id is not supported.
167
19
  */
168
20
  export async function getDepositBtcAddress({
@@ -192,41 +44,65 @@ export async function getDepositBtcAddress({
192
44
  try {
193
45
  if (isValidChain(chainId)) {
194
46
  const tokenContractInfo = await getTokenContractInfo(token, chainId, env);
195
-
196
47
  tokenAddressFilter = {
197
48
  token_address: tokenContractInfo.address.toLowerCase(),
198
- aux_version: token === Token.BTCK ? 1 : undefined,
199
49
  };
200
50
  }
51
+
52
+ if (isSuiChain(chainId)) {
53
+ const tokenAddress = getSuiTokenAddress(chainId, env);
54
+ if (tokenAddress) {
55
+ tokenAddressFilter = {
56
+ token_address: tokenAddress.toLowerCase(),
57
+ };
58
+ }
59
+ }
60
+
61
+ if (isSolanaChain(chainId)) {
62
+ const tokenAddress = getSolanaTokenAddress(chainId, env);
63
+ if (tokenAddress) {
64
+ tokenAddressFilter = {
65
+ token_address: tokenAddress.toLowerCase(),
66
+ };
67
+ }
68
+ }
201
69
  } catch {
202
70
  // NOOP
203
71
  }
204
72
 
205
- const addresses = (_addresses || []).filter(a => {
206
- if (!tokenAddressFilter) {
207
- // Get only the non token specific deposit addresses.
208
- return !a.deposit_metadata.token_address;
209
- }
73
+ const addresses = (_addresses || [])
74
+ .filter(
75
+ a =>
76
+ // filter by chain id
77
+ a.deposit_metadata.to_blockchain.toLowerCase() ===
78
+ getChainNameById(chainId).toLowerCase() &&
79
+ // filter by address
80
+ a.deposit_metadata.to_address.toLowerCase() === address.toLowerCase(),
81
+ )
82
+ .filter(a => {
83
+ if (!tokenAddressFilter) {
84
+ return false;
85
+ }
210
86
 
211
- // check if token addresses are matched
212
- let isForToken =
213
- a.deposit_metadata.token_address?.toLowerCase() ===
214
- tokenAddressFilter.token_address;
215
- // check if aux version is matched (if provided)
216
- if (tokenAddressFilter.aux_version != null) {
217
- isForToken =
218
- isForToken &&
219
- a.deposit_metadata.aux_version === tokenAddressFilter.aux_version;
220
- }
87
+ // check if token addresses are matched
88
+ let isForToken =
89
+ a.deposit_metadata.token_address?.toLowerCase() ===
90
+ tokenAddressFilter.token_address;
91
+ // check if aux version is matched (if provided)
92
+ if (tokenAddressFilter.aux_version != null) {
93
+ isForToken =
94
+ isForToken &&
95
+ a.deposit_metadata.aux_version === tokenAddressFilter.aux_version;
96
+ }
221
97
 
222
- // token_address can also be empty (null) when the address is for LBTC.
223
- if (token === Token.LBTC) {
224
- isForToken = isForToken || !a.deposit_metadata.token_address;
225
- }
98
+ // token_address can also be empty (null) when the address is for LBTC.
99
+ if (token === Token.LBTC) {
100
+ isForToken = isForToken || !a.deposit_metadata.token_address;
101
+ }
226
102
 
227
- // Get only the addresses for the specified token.
228
- return isForToken;
229
- });
103
+ // Get only the addresses for the specified token.
104
+ return isForToken;
105
+ });
230
106
 
231
107
  if (addresses && addresses.length > 0) {
232
108
  const mostRecentAddress = addresses.reduce((mostRecent, cur) => {
@@ -7,10 +7,8 @@ import { CodeBlock } from '../../stories/components/CodeBlock';
7
7
  import { functionType } from '../../stories/components/decorators';
8
8
  import { EXAMPLE_EVM_ADDRESS } from '../../stories/constants';
9
9
  import useQuery from '../../stories/hooks/useQuery';
10
- import {
11
- IGetDepositBtcAddressesParameters,
12
- getDepositBtcAddresses,
13
- } from './getDepositBtcAddress';
10
+ import { getDepositBtcAddresses } from './getDepositBtcAddress';
11
+ import { IGetDepositBtcAddressesParameters } from './types';
14
12
 
15
13
  const meta = {
16
14
  title: 'api/getDepositBtcAddresses',
@@ -0,0 +1,51 @@
1
+ import axios from 'axios';
2
+ import { getApiConfig } from '../../common/api-config';
3
+ import { getChainNameById } from '../../common/blockchain-identifier';
4
+ import {
5
+ IApiError,
6
+ IDepositAddressesResponse,
7
+ IGetDepositBtcAddressesParameters,
8
+ } from './types';
9
+
10
+ export async function makeRequest({
11
+ address,
12
+ chainId,
13
+ env,
14
+ limit,
15
+ offset,
16
+ partnerId,
17
+ }: IGetDepositBtcAddressesParameters) {
18
+ const { baseApiUrl } = getApiConfig(env);
19
+
20
+ // throws an error if `chainId` is unknown
21
+ const destinationBlockchain = getChainNameById(chainId);
22
+
23
+ const params = {
24
+ asc: false,
25
+ limit,
26
+ offset,
27
+ referralId: partnerId || 'lombard',
28
+ };
29
+
30
+ // remove undefined fields, undefined limit and offset params cause error
31
+ for (const [k, v] of Object.entries(params)) {
32
+ if (v === undefined) {
33
+ delete params[k as keyof typeof params];
34
+ }
35
+ }
36
+
37
+ const url = `api/v1/address/destination/${destinationBlockchain}/${address}`;
38
+ try {
39
+ const { data } = await axios.get<IDepositAddressesResponse>(url, {
40
+ baseURL: baseApiUrl,
41
+ params,
42
+ });
43
+
44
+ return data.addresses || [];
45
+ } catch (err) {
46
+ if (axios.isAxiosError<IApiError>(err)) {
47
+ const message = err.response?.data.message;
48
+ throw new Error(message);
49
+ }
50
+ }
51
+ }