@pear-protocol/exchanges-sdk 0.8.0 → 0.9.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.
@@ -0,0 +1,257 @@
1
+ import { isAddress } from 'viem';
2
+ import { parseUsdcAmount } from '../subaccount.helper';
3
+ import { assertOwnerWallet, readUsdcAmountSyntaxError, formatUsdcUnits, readDepositMinimumError, emitProgress, watchAccountRegistration, waitForDepositPoll, SETTLEMENT_WATCH_LIMIT_MS } from './deposit-settlement.helper';
4
+ import { getLighterNativeUsdcAddress, writeUsdcTransfer } from './ethereum/usdc.helper';
5
+ import { FUNXYZ_DEPOSIT_MINIMUMS_USD, FUNXYZ_EVM_CHAIN_NAMES, FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID } from './types';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ const FUNXYZ_COMPLETED_STATUS = "COMPLETED";
10
+ const FUNXYZ_PRE_ACCOUNT_RECIPIENT = "l1";
11
+ async function createFunxyzDepositAddress(sdk, params, demo) {
12
+ assertFunxyzEnvironment(demo);
13
+ if (!isAddress(params.ownerAddress)) {
14
+ throw new Error(`Invalid Lighter Fun.xyz owner address: ${params.ownerAddress}`);
15
+ }
16
+ const uda = await mintUda(sdk, params.ownerAddress, params.market, params.asset, params.accountIndex);
17
+ return {
18
+ route: "funxyz",
19
+ evmAddress: uda.evmAddress,
20
+ ...uda.raw.solanaAddr ? {
21
+ solanaAddress: uda.raw.solanaAddr
22
+ } : {},
23
+ ...uda.raw.tronAddr ? {
24
+ tronAddress: uda.raw.tronAddr
25
+ } : {},
26
+ ...uda.raw.btcAddrSegwit ? {
27
+ bitcoinSegwitAddress: uda.raw.btcAddrSegwit
28
+ } : {},
29
+ credits: uda.credits
30
+ };
31
+ }
32
+ __name(createFunxyzDepositAddress, "createFunxyzDepositAddress");
33
+ async function depositUsdcViaFunxyz(sdk, params, demo) {
34
+ assertFunxyzEnvironment(demo);
35
+ assertOwnerWallet(params.ownerAddress, params.connectedAddress);
36
+ assertFunxyzChain(params.chainId);
37
+ const usdcAddress = getLighterNativeUsdcAddress(params.chainId);
38
+ if (!usdcAddress) {
39
+ throw new Error(`This SDK cannot submit a Fun.xyz transfer on chain ID ${params.chainId}: its native USDC contract is not verified. Use createDepositAddress and send the funds yourself.`);
40
+ }
41
+ const syntaxError = readUsdcAmountSyntaxError(params.amount);
42
+ if (syntaxError) {
43
+ throw new Error(syntaxError);
44
+ }
45
+ const units = parseUsdcAmount(params.amount);
46
+ const balance = await params.wallet.readUsdcBalance(params.chainId, params.ownerAddress);
47
+ if (units > balance) {
48
+ throw new Error(`Your wallet holds ${formatUsdcUnits(balance)} USDC.`);
49
+ }
50
+ const uda = await mintUda(sdk, params.ownerAddress, params.market, "USDC", params.accountIndex);
51
+ const minimumError = readDepositMinimumError(units, readFunxyzMinimumUsd(readFunxyzChainSource(params.chainId), uda.credits.account));
52
+ if (minimumError) {
53
+ throw new Error(minimumError);
54
+ }
55
+ const wallet = await params.wallet.retrieveWallet(params.chainId);
56
+ if (wallet.account.address.toLowerCase() !== params.ownerAddress.toLowerCase()) {
57
+ throw new Error(`Connect the Lighter owner wallet ${params.ownerAddress} and try again.`);
58
+ }
59
+ if (wallet.chain?.id !== params.chainId) {
60
+ throw new Error(`Switch the wallet to chain ${params.chainId} before depositing.`);
61
+ }
62
+ const knownTxHashes = await readKnownBridgeTxHashes(sdk, params, uda.credits);
63
+ const txHash = await writeUsdcTransfer(wallet, {
64
+ usdcAddress,
65
+ to: uda.evmAddress,
66
+ amount: BigInt(units)
67
+ });
68
+ emitProgress(params.onProgress, params.signal, {
69
+ state: "confirming",
70
+ txHash
71
+ });
72
+ await params.wallet.confirmTransaction(params.chainId, txHash);
73
+ emitProgress(params.onProgress, params.signal, {
74
+ state: "bridging",
75
+ txHash
76
+ });
77
+ await followFunxyzSettlement(sdk, params, uda.credits, {
78
+ depositAddress: uda.evmAddress,
79
+ txHash,
80
+ chainId: params.chainId,
81
+ units
82
+ }, knownTxHashes, demo);
83
+ return {
84
+ route: "funxyz",
85
+ txHash,
86
+ depositAddress: uda.evmAddress,
87
+ credits: uda.credits
88
+ };
89
+ }
90
+ __name(depositUsdcViaFunxyz, "depositUsdcViaFunxyz");
91
+ function readFunxyzMinimumUsd(source, account) {
92
+ return FUNXYZ_DEPOSIT_MINIMUMS_USD[source][account];
93
+ }
94
+ __name(readFunxyzMinimumUsd, "readFunxyzMinimumUsd");
95
+ function listFunxyzTransferableChains() {
96
+ return Object.entries(FUNXYZ_EVM_CHAIN_NAMES).map(([chainId, name]) => ({
97
+ chainId: Number(chainId),
98
+ name
99
+ })).filter((chain) => getLighterNativeUsdcAddress(chain.chainId) !== void 0);
100
+ }
101
+ __name(listFunxyzTransferableChains, "listFunxyzTransferableChains");
102
+ function readFunxyzChainSource(chainId) {
103
+ return chainId === FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID ? "ethereum-mainnet" : "evm";
104
+ }
105
+ __name(readFunxyzChainSource, "readFunxyzChainSource");
106
+ async function mintUda(sdk, ownerAddress, market, asset, accountIndex) {
107
+ const { uda } = await sdk.core.misc.lighter.createUda({
108
+ walletAddress: ownerAddress,
109
+ market,
110
+ asset,
111
+ ...accountIndex === void 0 ? {} : {
112
+ accountIndex
113
+ }
114
+ });
115
+ if (uda.status === "blocked") {
116
+ throw new Error(`The Fun.xyz bridge has blocked ${ownerAddress}. Deposits from this wallet are not accepted.`);
117
+ }
118
+ const { depositAddr } = uda;
119
+ if (!isAddress(depositAddr)) {
120
+ throw new Error("The Fun.xyz bridge returned an invalid EVM deposit address.");
121
+ }
122
+ return {
123
+ evmAddress: depositAddr,
124
+ credits: readFunxyzCredit(uda.resolved, ownerAddress),
125
+ raw: uda
126
+ };
127
+ }
128
+ __name(mintUda, "mintUda");
129
+ function readFunxyzCredit(resolved, ownerAddress) {
130
+ if (resolved.userId.toLowerCase() !== ownerAddress.toLowerCase()) {
131
+ throw new Error(`The Fun.xyz bridge answered for ${resolved.userId}, not ${ownerAddress}. Deposits from this wallet are not accepted.`);
132
+ }
133
+ if (/^\d+$/.test(resolved.recipientAddr)) {
134
+ const accountIndex = Number(resolved.recipientAddr);
135
+ if (!Number.isSafeInteger(accountIndex)) {
136
+ throw new Error(`The Fun.xyz bridge returned an unusable Lighter account index: ${resolved.recipientAddr}`);
137
+ }
138
+ return {
139
+ account: "existing",
140
+ accountIndex
141
+ };
142
+ }
143
+ const recipient = resolved.recipientAddr.toLowerCase();
144
+ if (recipient !== FUNXYZ_PRE_ACCOUNT_RECIPIENT && recipient !== ownerAddress.toLowerCase()) {
145
+ throw new Error(`The Fun.xyz bridge named an unreadable Lighter recipient: ${resolved.recipientAddr}. Expected an account index or "${FUNXYZ_PRE_ACCOUNT_RECIPIENT}".`);
146
+ }
147
+ return {
148
+ account: "new",
149
+ ownerAddress
150
+ };
151
+ }
152
+ __name(readFunxyzCredit, "readFunxyzCredit");
153
+ function assertFunxyzChain(chainId) {
154
+ if (!(chainId in FUNXYZ_EVM_CHAIN_NAMES)) {
155
+ const supported = Object.entries(FUNXYZ_EVM_CHAIN_NAMES).map(([id, name]) => `${name} (${id})`).join(", ");
156
+ throw new Error(`Unsupported Lighter Fun.xyz chain ID: ${chainId}. Fun.xyz accepts ${supported}.`);
157
+ }
158
+ }
159
+ __name(assertFunxyzChain, "assertFunxyzChain");
160
+ function assertFunxyzEnvironment(demo) {
161
+ if (demo) {
162
+ throw new Error("Lighter demo Fun.xyz deposits are unavailable because the bridge serves mainnet only");
163
+ }
164
+ }
165
+ __name(assertFunxyzEnvironment, "assertFunxyzEnvironment");
166
+ async function followFunxyzSettlement(sdk, params, credits, transfer, knownTxHashes, demo) {
167
+ if (!params.settlement) {
168
+ emitProgress(params.onProgress, params.signal, {
169
+ state: "untracked",
170
+ txHash: transfer.txHash
171
+ });
172
+ return;
173
+ }
174
+ switch (credits.account) {
175
+ case "existing": {
176
+ const transaction = await watchFunxyzBridge(sdk, params, transfer, knownTxHashes);
177
+ if (!transaction) {
178
+ return;
179
+ }
180
+ emitProgress(params.onProgress, params.signal, {
181
+ state: "credited",
182
+ txHash: transfer.txHash,
183
+ credit: {
184
+ source: "funxyz-bridge",
185
+ transaction
186
+ }
187
+ });
188
+ return;
189
+ }
190
+ case "new": {
191
+ await watchAccountRegistration(params.ownerAddress, transfer.txHash, params.onProgress, params.signal, demo);
192
+ return;
193
+ }
194
+ }
195
+ }
196
+ __name(followFunxyzSettlement, "followFunxyzSettlement");
197
+ async function readKnownBridgeTxHashes(sdk, params, credits) {
198
+ if (!params.settlement || credits.account !== "existing") {
199
+ return void 0;
200
+ }
201
+ try {
202
+ const result = await sdk.core.misc.lighter.listUdaDeposits(params.ownerAddress);
203
+ return new Set(result.transactions.map((transaction) => transaction.txHash.toLowerCase()));
204
+ } catch {
205
+ return void 0;
206
+ }
207
+ }
208
+ __name(readKnownBridgeTxHashes, "readKnownBridgeTxHashes");
209
+ async function watchFunxyzBridge(sdk, params, transfer, knownTxHashes) {
210
+ const signal = params.signal;
211
+ const deadline = Date.now() + SETTLEMENT_WATCH_LIMIT_MS;
212
+ while (!signal?.aborted) {
213
+ await waitForDepositPoll(signal);
214
+ if (signal?.aborted) {
215
+ return void 0;
216
+ }
217
+ if (Date.now() >= deadline) {
218
+ emitProgress(params.onProgress, signal, {
219
+ state: "unsettled",
220
+ txHash: transfer.txHash
221
+ });
222
+ return void 0;
223
+ }
224
+ let transactions;
225
+ try {
226
+ const result = await sdk.core.misc.lighter.listUdaDeposits(params.ownerAddress);
227
+ transactions = result.transactions;
228
+ } catch {
229
+ continue;
230
+ }
231
+ const transaction = findFunxyzTransaction(transactions, transfer, knownTxHashes);
232
+ if (!transaction || transaction.status !== FUNXYZ_COMPLETED_STATUS) {
233
+ emitProgress(params.onProgress, signal, {
234
+ state: "bridging",
235
+ txHash: transfer.txHash
236
+ });
237
+ continue;
238
+ }
239
+ return transaction;
240
+ }
241
+ return void 0;
242
+ }
243
+ __name(watchFunxyzBridge, "watchFunxyzBridge");
244
+ function findFunxyzTransaction(transactions, transfer, knownTxHashes) {
245
+ const byHash = transactions.find((candidate) => candidate.txHash.toLowerCase() === transfer.txHash.toLowerCase());
246
+ if (byHash) {
247
+ return byHash;
248
+ }
249
+ if (!knownTxHashes) {
250
+ return void 0;
251
+ }
252
+ const candidates = transactions.filter((candidate) => candidate.depositAddr.toLowerCase() === transfer.depositAddress.toLowerCase() && !knownTxHashes.has(candidate.txHash.toLowerCase()) && candidate.fromChainId === String(transfer.chainId) && candidate.fromAmountBaseUnit === String(transfer.units));
253
+ return candidates.length === 1 ? candidates[0] : void 0;
254
+ }
255
+ __name(findFunxyzTransaction, "findFunxyzTransaction");
256
+
257
+ export { createFunxyzDepositAddress, depositUsdcViaFunxyz, listFunxyzTransferableChains, readFunxyzChainSource, readFunxyzMinimumUsd };
@@ -1,10 +1,12 @@
1
- import { parseUnits, isAddress, parseEther } from 'viem';
2
- import { fetchApiKeys, fetchAccount, fetchAccountsByL1Address, fetchAssetDetails } from '../../client/lighter/rest';
1
+ import { parseUnits, isAddress } from 'viem';
2
+ import { fetchApiKeys, fetchAccount, fetchPublicAccount, fetchAccountsByL1Address } from '../../client/lighter/rest';
3
3
  import { parseUsdcAmount } from '../subaccount.helper';
4
- import { depositUsdcViaCctp, createExternalCctpDepositIntent, listCctpDepositNetworks } from './cctp-deposit.helper';
5
- import { writeWithdraw, writeDeposit } from './ethereum';
4
+ import { resolveAssetDecimals } from './asset-decimals.helper';
5
+ import { listCctpDepositNetworks } from './cctp-deposit.helper';
6
+ import { createDepositAddressForRoute, depositThroughRoute } from './deposit.helper';
7
+ import { writeWithdraw } from './ethereum';
6
8
  import { LIGHTER_ACCOUNT_NOT_REGISTERED_CODE, readOnboardingStatus } from './onboarding.helper';
7
- import { MIN_LIGHTER_FAST_WITHDRAW_USDC, AssetId } from './types';
9
+ import { MIN_LIGHTER_FAST_WITHDRAW_USDC } from './types';
8
10
 
9
11
  var __defProp = Object.defineProperty;
10
12
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -43,9 +45,9 @@ function createLighterHelper(sdk, demo = false) {
43
45
  subAccountIndexes: accounts.filter((s) => s.account_type !== 0).map((s) => s.index)
44
46
  };
45
47
  },
46
- async checkIntegratorApproval(accountIndex, integratorAccountIndex, requirements) {
48
+ async checkIntegratorApproval(accountIndex, integratorAccountIndex, requirements, authToken) {
47
49
  if (requirements) validateApprovalRequirements(requirements);
48
- const data = await fetchAccount(accountIndex, demo);
50
+ const data = authToken ? await fetchAccount(accountIndex, authToken, demo) : await fetchPublicAccount(accountIndex, demo);
49
51
  const account = data.accounts[0];
50
52
  if (!account) return false;
51
53
  const minMakerFee = requirements?.minPerpsMakerFee ?? 0;
@@ -102,45 +104,41 @@ function createLighterHelper(sdk, demo = false) {
102
104
  txHash
103
105
  };
104
106
  },
105
- /** Lists the source networks Lighter credits through an intent address right now. */
106
- async listDepositNetworks() {
107
- return listCctpDepositNetworks(demo);
108
- },
109
- /** Creates a CCTP intent address for USDC deposits from a currently supported chain. */
110
- async createExternalCctpDepositIntent(params) {
111
- return createExternalCctpDepositIntent(params, demo);
112
- },
113
- /** Runs the complete direct native-USDC deposit flow. */
114
- async depositUsdcViaCctp(params) {
115
- return depositUsdcViaCctp(sdk, params, demo);
116
- },
117
- async deposit(walletClient, params) {
107
+ /**
108
+ * Moves the account onto another Lighter tier.
109
+ *
110
+ * Lighter allows this once every 24 hours, and only while the account holds no open orders or
111
+ * positions. It refuses the change itself and says which rule stopped it, so that wording
112
+ * arrives as the thrown error rather than being guessed at here.
113
+ */
114
+ async changeAccountTier(params) {
118
115
  if (demo) {
119
- throw new Error("Lighter demo deposits are unavailable because the testnet gateway is not verified");
120
- }
121
- const l1Decimals = await resolveAssetDecimals(params.assetId, "l1_decimals", params.l1Decimals, demo);
122
- const amount = parseUnits(params.amount, l1Decimals);
123
- let value;
124
- if (params.assetId === AssetId.ETH) {
125
- value = parseEther(params.nativeValue ?? params.amount);
126
- if (value !== amount) {
127
- throw new Error("Lighter native ETH deposit amount and msg.value must match");
128
- }
129
- } else if (params.nativeValue !== void 0 && parseEther(params.nativeValue) !== 0n) {
130
- throw new Error("Lighter nativeValue is only valid for ETH deposits");
116
+ throw new Error("Lighter demo tier changes are unavailable because backend signing is mainnet-only");
131
117
  }
132
- const txHash = await writeDeposit(walletClient, {
133
- to: params.to,
134
- assetIndex: params.assetId,
135
- routeType: params.routeType,
136
- // Ethereum deposits use the L1 token precision, not Lighter's L2/base-amount precision.
137
- amount,
138
- value
118
+ const result = await sdk.core.misc.lighter.changeAccountTier({
119
+ accountIndex: params.accountIndex,
120
+ apiKeyIndex: params.apiKeyIndex,
121
+ apiPrivateKey: params.apiPrivateKey,
122
+ tier: params.tier
139
123
  });
140
124
  return {
141
- txHash
125
+ tier: result.tier
142
126
  };
143
127
  },
128
+ /** Lists the source networks Lighter credits through a CCTP intent address right now. */
129
+ async listCctpDepositNetworks() {
130
+ return listCctpDepositNetworks(demo);
131
+ },
132
+ /** Runs the deposit route the caller selected, from validation through settlement. */
133
+ async deposit(params) {
134
+ const result = await depositThroughRoute(sdk, params, demo);
135
+ return result;
136
+ },
137
+ /** Hands out an address any wallet or exchange may fund, for the route the caller selected. */
138
+ async createDepositAddress(params) {
139
+ const result = await createDepositAddressForRoute(sdk, params, demo);
140
+ return result;
141
+ },
144
142
  /** Performs a wallet-authorized fast USDC withdrawal through Lighter's transfer pool. */
145
143
  async fastWithdraw(walletClient, params) {
146
144
  if (demo) {
@@ -196,21 +194,6 @@ function createLighterHelper(sdk, demo = false) {
196
194
  return helper;
197
195
  }
198
196
  __name(createLighterHelper, "createLighterHelper");
199
- function validateDecimals(decimals, source) {
200
- if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > 255) {
201
- throw new Error(`Invalid ${source} decimals: ${decimals}`);
202
- }
203
- return decimals;
204
- }
205
- __name(validateDecimals, "validateDecimals");
206
- async function resolveAssetDecimals(assetId, field, suppliedDecimals, demo) {
207
- if (suppliedDecimals !== void 0) return validateDecimals(suppliedDecimals, field);
208
- const assets = await fetchAssetDetails(demo);
209
- const asset = assets.find((candidate) => candidate.asset_id === assetId);
210
- if (!asset) throw new Error(`Lighter asset ${assetId} was not returned by assetDetails`);
211
- return validateDecimals(asset[field], `assetDetails.${field}`);
212
- }
213
- __name(resolveAssetDecimals, "resolveAssetDecimals");
214
197
  function validateApprovalRequirements(requirements) {
215
198
  for (const [name, value] of [
216
199
  [
@@ -1,3 +1,5 @@
1
- export { LIGHTER_CCTP_USDC_ADDRESSES, LIGHTER_CCTP_USDC_DECIMALS, getLighterCctpUsdcAddress, usdcAbi } from './ethereum/usdc.helper';
1
+ export { retrieveLighterAuthToken } from './auth-token.helper';
2
+ export { LIGHTER_NATIVE_USDC_ADDRESSES, LIGHTER_USDC_DECIMALS, getLighterNativeUsdcAddress, usdcAbi } from './ethereum/usdc.helper';
3
+ export { listFunxyzTransferableChains, readFunxyzChainSource, readFunxyzMinimumUsd } from './funxyz-deposit.helper';
2
4
  export * from './helper';
3
5
  export * from './types';
@@ -37,5 +37,38 @@ function getAssetDecimals(assetId) {
37
37
  return decimals;
38
38
  }
39
39
  __name(getAssetDecimals, "getAssetDecimals");
40
+ const FUNXYZ_EVM_CHAIN_NAMES = {
41
+ 1: "Ethereum",
42
+ 10: "Optimism",
43
+ 137: "Polygon",
44
+ 143: "Monad",
45
+ 999: "HyperEVM",
46
+ 8453: "Base",
47
+ 42161: "Arbitrum One",
48
+ 57073: "Ink"
49
+ };
50
+ const FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID = 1;
51
+ const FUNXYZ_DEPOSIT_MINIMUMS_USD = {
52
+ "ethereum-mainnet": {
53
+ new: "5",
54
+ existing: "5"
55
+ },
56
+ evm: {
57
+ new: "5",
58
+ existing: "3"
59
+ },
60
+ solana: {
61
+ new: "5",
62
+ existing: "3"
63
+ },
64
+ tron: {
65
+ new: "5",
66
+ existing: "5"
67
+ },
68
+ bitcoin: {
69
+ new: "10",
70
+ existing: "10"
71
+ }
72
+ };
40
73
 
41
- export { ASSET_DECIMALS, AssetId, L2_ASSET_DECIMALS, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_USDC, RouteType, getAssetDecimals };
74
+ export { ASSET_DECIMALS, AssetId, FUNXYZ_DEPOSIT_MINIMUMS_USD, FUNXYZ_ETHEREUM_MAINNET_CHAIN_ID, FUNXYZ_EVM_CHAIN_NAMES, L2_ASSET_DECIMALS, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_USDC, RouteType, getAssetDecimals };