@liquidium/client 0.7.0 → 0.8.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 +2 -0
- package/dist/index.cjs +112 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +112 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Agent, Identity } from '@icp-sdk/core/agent';
|
|
2
2
|
import { PublicClient } from 'viem';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Minimal viem-compatible client shape required for SDK EVM read calls.
|
|
6
|
+
*
|
|
7
|
+
* When `getCode` is present and `chain` identifies Ethereum mainnet, the SDK
|
|
8
|
+
* uses it for a best-effort native ETH contract-destination check. Provider
|
|
9
|
+
* failures fail open and do not block the outflow.
|
|
10
|
+
*/
|
|
11
|
+
interface EvmReadClient {
|
|
12
|
+
readContract: PublicClient["readContract"];
|
|
13
|
+
chain?: PublicClient["chain"];
|
|
14
|
+
getCode?: PublicClient["getCode"];
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Runtime options for `new LiquidiumClient(config)`.
|
|
8
18
|
*
|
|
@@ -12,6 +22,8 @@ type EvmReadClient = Pick<PublicClient, "readContract">;
|
|
|
12
22
|
interface LiquidiumClientConfig {
|
|
13
23
|
/** Preset canister IDs. Only `mainnet` is bundled. */
|
|
14
24
|
environment?: Environment;
|
|
25
|
+
/** Preconfigured ICP agent. Takes precedence over `icHost` and `identity`. */
|
|
26
|
+
agent?: Agent;
|
|
15
27
|
/** ICP replica host override (defaults follow `@icp-sdk/core/agent`). */
|
|
16
28
|
icHost?: string;
|
|
17
29
|
/** Agent identity for signed canister calls. */
|
|
@@ -30,11 +42,11 @@ interface LiquidiumClientConfig {
|
|
|
30
42
|
fetch?: typeof fetch;
|
|
31
43
|
/** Per-request timeout for SDK API calls in milliseconds. */
|
|
32
44
|
timeoutMs?: number;
|
|
33
|
-
/** Ethereum RPC URL used for public ERC-20 reads
|
|
45
|
+
/** Ethereum RPC URL used for best-effort native ETH outflow checks and public ERC-20 reads. */
|
|
34
46
|
evmRpcUrl?: string;
|
|
35
47
|
/** Optional headers for RPC providers that authenticate via HTTP headers. */
|
|
36
48
|
evmRpcHeaders?: Record<string, string>;
|
|
37
|
-
/** Existing viem
|
|
49
|
+
/** Existing viem client; mainnet `chain` and `getCode` enable native ETH outflow checks. */
|
|
38
50
|
evmPublicClient?: EvmReadClient;
|
|
39
51
|
}
|
|
40
52
|
/** Pool canister principal text values grouped by pool asset. */
|
|
@@ -168,7 +180,7 @@ interface Wallet {
|
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
interface CanisterContext {
|
|
171
|
-
agent:
|
|
183
|
+
agent: Agent;
|
|
172
184
|
canisterIds: CanisterIds;
|
|
173
185
|
}
|
|
174
186
|
|
|
@@ -2305,6 +2317,7 @@ declare const LiquidiumErrorCode: {
|
|
|
2305
2317
|
readonly WITHDRAW_TOO_LOW: "WITHDRAW_TOO_LOW";
|
|
2306
2318
|
readonly REPAYMENT_EXCEEDS_DEBT: "REPAYMENT_EXCEEDS_DEBT";
|
|
2307
2319
|
readonly INVALID_ADDRESS: "INVALID_ADDRESS";
|
|
2320
|
+
readonly CONTRACT_DESTINATION_UNSUPPORTED: "CONTRACT_DESTINATION_UNSUPPORTED";
|
|
2308
2321
|
readonly DEPOSIT_ADDRESS_ERROR: "DEPOSIT_ADDRESS_ERROR";
|
|
2309
2322
|
readonly NETWORK_ERROR: "NETWORK_ERROR";
|
|
2310
2323
|
readonly SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Agent, Identity } from '@icp-sdk/core/agent';
|
|
2
2
|
import { PublicClient } from 'viem';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Minimal viem-compatible client shape required for SDK EVM read calls.
|
|
6
|
+
*
|
|
7
|
+
* When `getCode` is present and `chain` identifies Ethereum mainnet, the SDK
|
|
8
|
+
* uses it for a best-effort native ETH contract-destination check. Provider
|
|
9
|
+
* failures fail open and do not block the outflow.
|
|
10
|
+
*/
|
|
11
|
+
interface EvmReadClient {
|
|
12
|
+
readContract: PublicClient["readContract"];
|
|
13
|
+
chain?: PublicClient["chain"];
|
|
14
|
+
getCode?: PublicClient["getCode"];
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Runtime options for `new LiquidiumClient(config)`.
|
|
8
18
|
*
|
|
@@ -12,6 +22,8 @@ type EvmReadClient = Pick<PublicClient, "readContract">;
|
|
|
12
22
|
interface LiquidiumClientConfig {
|
|
13
23
|
/** Preset canister IDs. Only `mainnet` is bundled. */
|
|
14
24
|
environment?: Environment;
|
|
25
|
+
/** Preconfigured ICP agent. Takes precedence over `icHost` and `identity`. */
|
|
26
|
+
agent?: Agent;
|
|
15
27
|
/** ICP replica host override (defaults follow `@icp-sdk/core/agent`). */
|
|
16
28
|
icHost?: string;
|
|
17
29
|
/** Agent identity for signed canister calls. */
|
|
@@ -30,11 +42,11 @@ interface LiquidiumClientConfig {
|
|
|
30
42
|
fetch?: typeof fetch;
|
|
31
43
|
/** Per-request timeout for SDK API calls in milliseconds. */
|
|
32
44
|
timeoutMs?: number;
|
|
33
|
-
/** Ethereum RPC URL used for public ERC-20 reads
|
|
45
|
+
/** Ethereum RPC URL used for best-effort native ETH outflow checks and public ERC-20 reads. */
|
|
34
46
|
evmRpcUrl?: string;
|
|
35
47
|
/** Optional headers for RPC providers that authenticate via HTTP headers. */
|
|
36
48
|
evmRpcHeaders?: Record<string, string>;
|
|
37
|
-
/** Existing viem
|
|
49
|
+
/** Existing viem client; mainnet `chain` and `getCode` enable native ETH outflow checks. */
|
|
38
50
|
evmPublicClient?: EvmReadClient;
|
|
39
51
|
}
|
|
40
52
|
/** Pool canister principal text values grouped by pool asset. */
|
|
@@ -168,7 +180,7 @@ interface Wallet {
|
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
interface CanisterContext {
|
|
171
|
-
agent:
|
|
183
|
+
agent: Agent;
|
|
172
184
|
canisterIds: CanisterIds;
|
|
173
185
|
}
|
|
174
186
|
|
|
@@ -2305,6 +2317,7 @@ declare const LiquidiumErrorCode: {
|
|
|
2305
2317
|
readonly WITHDRAW_TOO_LOW: "WITHDRAW_TOO_LOW";
|
|
2306
2318
|
readonly REPAYMENT_EXCEEDS_DEBT: "REPAYMENT_EXCEEDS_DEBT";
|
|
2307
2319
|
readonly INVALID_ADDRESS: "INVALID_ADDRESS";
|
|
2320
|
+
readonly CONTRACT_DESTINATION_UNSUPPORTED: "CONTRACT_DESTINATION_UNSUPPORTED";
|
|
2308
2321
|
readonly DEPOSIT_ADDRESS_ERROR: "DEPOSIT_ADDRESS_ERROR";
|
|
2309
2322
|
readonly NETWORK_ERROR: "NETWORK_ERROR";
|
|
2310
2323
|
readonly SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE";
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,7 @@ var LiquidiumErrorCode = {
|
|
|
45
45
|
WITHDRAW_TOO_LOW: "WITHDRAW_TOO_LOW",
|
|
46
46
|
REPAYMENT_EXCEEDS_DEBT: "REPAYMENT_EXCEEDS_DEBT",
|
|
47
47
|
INVALID_ADDRESS: "INVALID_ADDRESS",
|
|
48
|
+
CONTRACT_DESTINATION_UNSUPPORTED: "CONTRACT_DESTINATION_UNSUPPORTED",
|
|
48
49
|
DEPOSIT_ADDRESS_ERROR: "DEPOSIT_ADDRESS_ERROR",
|
|
49
50
|
// Transport
|
|
50
51
|
NETWORK_ERROR: "NETWORK_ERROR",
|
|
@@ -318,6 +319,12 @@ function appendErrorContext(message, context) {
|
|
|
318
319
|
return `${message} (${details.join(", ")})`;
|
|
319
320
|
}
|
|
320
321
|
function createCanisterContext(opts) {
|
|
322
|
+
if (opts.agent) {
|
|
323
|
+
return {
|
|
324
|
+
agent: opts.agent,
|
|
325
|
+
canisterIds: opts.canisterIds
|
|
326
|
+
};
|
|
327
|
+
}
|
|
321
328
|
const host = resolveHost(opts.icHost);
|
|
322
329
|
const agent = HttpAgent.createSync({
|
|
323
330
|
host,
|
|
@@ -1785,6 +1792,11 @@ var HISTORY_ACTIVITIES = `${SDK_API_V2_PATH}/history/activities`;
|
|
|
1785
1792
|
var HISTORY_USERS = `${SDK_API_V2_PATH}/history/users`;
|
|
1786
1793
|
var INFLOW = `${SDK_API_V2_PATH}/inflow`;
|
|
1787
1794
|
var SIMPLE_LOANS = `${SDK_API_V1_PATH}/instant-loans`;
|
|
1795
|
+
function buildEthereumAddressBytecodePath(request) {
|
|
1796
|
+
return `${SDK_API_V2_PATH}/ethereum/addresses/${encodeURIComponent(
|
|
1797
|
+
request.address
|
|
1798
|
+
)}/bytecode`;
|
|
1799
|
+
}
|
|
1788
1800
|
function buildHistoryUserTransactionsPath(profileId, query) {
|
|
1789
1801
|
const base = `${HISTORY_USERS}/${encodeURIComponent(profileId)}/transactions`;
|
|
1790
1802
|
const qs = query.toString();
|
|
@@ -3341,6 +3353,71 @@ function accountTypeToString(accountType) {
|
|
|
3341
3353
|
return `Principal:${accountType.data}`;
|
|
3342
3354
|
}
|
|
3343
3355
|
}
|
|
3356
|
+
var EMPTY_EVM_BYTECODE = "0x";
|
|
3357
|
+
var LAST_ETHEREUM_MAINNET_PRECOMPILE_ADDRESS = 0x11n;
|
|
3358
|
+
var CONTRACT_DESTINATION_UNSUPPORTED_MESSAGE = "Contract addresses are not supported for native ETH withdrawals or borrowing";
|
|
3359
|
+
var RESERVED_DESTINATION_MESSAGE = "Ethereum outflow destination must not be the zero address or a precompile";
|
|
3360
|
+
async function guardEthereumOutflowDestination({
|
|
3361
|
+
address,
|
|
3362
|
+
apiClient,
|
|
3363
|
+
asset,
|
|
3364
|
+
chain,
|
|
3365
|
+
evmReadClient
|
|
3366
|
+
}) {
|
|
3367
|
+
if (chain !== Chain.ETH) {
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3370
|
+
const normalizedAddress = getAddress(address);
|
|
3371
|
+
if (BigInt(normalizedAddress) <= LAST_ETHEREUM_MAINNET_PRECOMPILE_ADDRESS) {
|
|
3372
|
+
throw new LiquidiumError(
|
|
3373
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
3374
|
+
RESERVED_DESTINATION_MESSAGE
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3377
|
+
if (asset !== Asset.ETH) {
|
|
3378
|
+
return;
|
|
3379
|
+
}
|
|
3380
|
+
if (evmReadClient?.chain?.id === mainnet.id && evmReadClient.getCode) {
|
|
3381
|
+
let bytecode;
|
|
3382
|
+
try {
|
|
3383
|
+
bytecode = await evmReadClient.getCode({
|
|
3384
|
+
address: normalizedAddress
|
|
3385
|
+
});
|
|
3386
|
+
} catch {
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
if (bytecode === void 0 || bytecode === EMPTY_EVM_BYTECODE) {
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3392
|
+
if (typeof bytecode !== "string" || !/^0x(?:[0-9a-fA-F]{2})*$/.test(bytecode)) {
|
|
3393
|
+
return;
|
|
3394
|
+
}
|
|
3395
|
+
throw new LiquidiumError(
|
|
3396
|
+
LiquidiumErrorCode.CONTRACT_DESTINATION_UNSUPPORTED,
|
|
3397
|
+
CONTRACT_DESTINATION_UNSUPPORTED_MESSAGE
|
|
3398
|
+
);
|
|
3399
|
+
}
|
|
3400
|
+
if (!apiClient) {
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
let response;
|
|
3404
|
+
try {
|
|
3405
|
+
response = await apiClient.get(
|
|
3406
|
+
buildEthereumAddressBytecodePath({ address: normalizedAddress })
|
|
3407
|
+
);
|
|
3408
|
+
} catch {
|
|
3409
|
+
return;
|
|
3410
|
+
}
|
|
3411
|
+
if (!response || typeof response !== "object" || !("hasDeployedBytecode" in response) || typeof response.hasDeployedBytecode !== "boolean") {
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3414
|
+
if (response.hasDeployedBytecode) {
|
|
3415
|
+
throw new LiquidiumError(
|
|
3416
|
+
LiquidiumErrorCode.CONTRACT_DESTINATION_UNSUPPORTED,
|
|
3417
|
+
CONTRACT_DESTINATION_UNSUPPORTED_MESSAGE
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3344
3421
|
|
|
3345
3422
|
// src/core/pool-ledger-assets.ts
|
|
3346
3423
|
function getPoolLedgerAssetRoute(params) {
|
|
@@ -4723,6 +4800,13 @@ var LendingModule = class {
|
|
|
4723
4800
|
poolChain: selectedPool.chain,
|
|
4724
4801
|
destinationChain: request.chain
|
|
4725
4802
|
});
|
|
4803
|
+
await guardEthereumOutflowDestination({
|
|
4804
|
+
address: receiver.address,
|
|
4805
|
+
apiClient: this.apiClient,
|
|
4806
|
+
asset: selectedAsset,
|
|
4807
|
+
chain: request.chain,
|
|
4808
|
+
evmReadClient: this.evmReadClient
|
|
4809
|
+
});
|
|
4726
4810
|
const lendingActor = createLendingActor(this.canisterContext);
|
|
4727
4811
|
try {
|
|
4728
4812
|
const expiryTimestamp = computeExpiryTimestampFromNow();
|
|
@@ -4741,7 +4825,9 @@ var LendingModule = class {
|
|
|
4741
4825
|
};
|
|
4742
4826
|
const withdrawSubmissionData = {
|
|
4743
4827
|
...withdrawActionData,
|
|
4744
|
-
|
|
4828
|
+
asset: selectedAsset,
|
|
4829
|
+
receiverAccount: receiver.canisterAccount,
|
|
4830
|
+
receiverAddress: receiver.address
|
|
4745
4831
|
};
|
|
4746
4832
|
return {
|
|
4747
4833
|
kind: WalletActionKind.createWithdraw,
|
|
@@ -4773,6 +4859,13 @@ var LendingModule = class {
|
|
|
4773
4859
|
}
|
|
4774
4860
|
}
|
|
4775
4861
|
async submitWithdraw(request, signatureInfo) {
|
|
4862
|
+
await guardEthereumOutflowDestination({
|
|
4863
|
+
address: request.receiverAddress,
|
|
4864
|
+
apiClient: this.apiClient,
|
|
4865
|
+
asset: request.asset,
|
|
4866
|
+
chain: request.chain,
|
|
4867
|
+
evmReadClient: this.evmReadClient
|
|
4868
|
+
});
|
|
4776
4869
|
try {
|
|
4777
4870
|
const result = await createLendingActor(this.canisterContext).withdraw(
|
|
4778
4871
|
Principal.fromText(request.profileId),
|
|
@@ -4877,6 +4970,13 @@ var LendingModule = class {
|
|
|
4877
4970
|
poolChain: selectedPool.chain,
|
|
4878
4971
|
destinationChain: request.chain
|
|
4879
4972
|
});
|
|
4973
|
+
await guardEthereumOutflowDestination({
|
|
4974
|
+
address: receiver.address,
|
|
4975
|
+
apiClient: this.apiClient,
|
|
4976
|
+
asset: selectedAsset,
|
|
4977
|
+
chain: request.chain,
|
|
4978
|
+
evmReadClient: this.evmReadClient
|
|
4979
|
+
});
|
|
4880
4980
|
await this.guardBorrowSameAssetPolicy({
|
|
4881
4981
|
profileId: request.profileId,
|
|
4882
4982
|
pool: selectedPool
|
|
@@ -4899,7 +4999,9 @@ var LendingModule = class {
|
|
|
4899
4999
|
};
|
|
4900
5000
|
const borrowSubmissionData = {
|
|
4901
5001
|
...borrowActionData,
|
|
4902
|
-
|
|
5002
|
+
asset: selectedAsset,
|
|
5003
|
+
receiverAccount: receiver.canisterAccount,
|
|
5004
|
+
receiverAddress: receiver.address
|
|
4903
5005
|
};
|
|
4904
5006
|
return {
|
|
4905
5007
|
kind: WalletActionKind.createBorrow,
|
|
@@ -4928,6 +5030,13 @@ var LendingModule = class {
|
|
|
4928
5030
|
}
|
|
4929
5031
|
}
|
|
4930
5032
|
async submitBorrow(request, signatureInfo) {
|
|
5033
|
+
await guardEthereumOutflowDestination({
|
|
5034
|
+
address: request.receiverAddress,
|
|
5035
|
+
apiClient: this.apiClient,
|
|
5036
|
+
asset: request.asset,
|
|
5037
|
+
chain: request.chain,
|
|
5038
|
+
evmReadClient: this.evmReadClient
|
|
5039
|
+
});
|
|
4931
5040
|
try {
|
|
4932
5041
|
const result = await createLendingActor(
|
|
4933
5042
|
this.canisterContext
|
|
@@ -7620,6 +7729,7 @@ var LiquidiumClient = class {
|
|
|
7620
7729
|
const canisterIds = resolveCanisterIds(environment, config.canisterIds);
|
|
7621
7730
|
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
7622
7731
|
this.canisterContext = createCanisterContext({
|
|
7732
|
+
agent: config.agent,
|
|
7623
7733
|
icHost: config.icHost,
|
|
7624
7734
|
identity: config.identity,
|
|
7625
7735
|
canisterIds
|