@pear-protocol/exchanges-sdk 0.17.1 → 0.19.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 CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  Unified SDK for account state (balance, positions, per-asset leverage) across supported derivative exchanges — as a live WebSocket subscription or a stateless one-shot snapshot.
4
4
 
5
+ ## Public trading equity
6
+
7
+ ```typescript
8
+ const trader = await sdk.core.traders.public(tradeAccountId);
9
+ const equity = await exchanges.tradingEquity(trader);
10
+ console.log(equity.tradingEquityUsd, equity.timestamp);
11
+ ```
12
+
13
+ Pass the returned public identity directly; only `connector` and `exchangeIdentifier` are used.
14
+ This read supports Hyperliquid, Lighter, and Lighter RHC without credentials or starting a stream.
15
+ It includes unrealized PnL and spot holdings, excludes staking and vault deposits, and rejects
16
+ failed reads or unpriceable holdings. Equity refreshes do not reload the public trader resources.
17
+
18
+ The same public identity can create a tracker for explicit REST snapshots:
19
+
20
+ ```typescript
21
+ const tracker = exchanges.createTracker(trader, { access: 'public' });
22
+ const snapshot = await tracker.snapshot();
23
+ const equity = await tracker.tradingEquity();
24
+ ```
25
+
26
+ Public Lighter trackers support REST reads only. Private account streams and tier reads still
27
+ require an authenticated connection. Existing `createTracker(connection)` calls keep their behavior.
28
+
5
29
  ## Supported Exchanges
6
30
 
7
31
  | Exchange | Connector | Market |
@@ -279,6 +303,8 @@ tracker.trackBalance((balance) => {
279
303
 
280
304
  On the three venues whose pools are separate *and* whose account-level figures cover one currency only — Binance single-asset, OKX Spot and OKX Futures — the core scalars carry the venue's own figure when exactly one pool holds a non-zero balance, and `null` once two or more do. They are never summed across pools. Hyperliquid splits by mode: Standard and Unified report `poolsShared: false` with one row per collateral token, while Portfolio Margin reports `true` — it lends against every holding at that coin's LTV, so the rows are holdings in one pool. All three keep their scalars in every case: `availableMargin` is already a per-asset floor.
281
305
 
306
+ **Hyperliquid also lists the whole spot wallet.** Every Hyperliquid variant carries `spotHoldings`: one row per coin the venue lists, in every mode, including coins no perp dex settles in (which `perCollateral` leaves out in Standard and Unified). A row carries `asset`, `token`, and the venue's signed `total`, `hold` and `entryNotional`. `entryNotional` is what the holding cost, not what it is worth, and is `null` when the venue sends none. Rows are never priced: value the wallet with the variant's equity scalar.
307
+
282
308
  #### Narrowing on `accountMode`
283
309
 
284
310
  Fields beyond the core belong to a variant. Switch on the tag to reach them, and write no `default:` clause — then a mode added in a later release fails the build here instead of silently returning `undefined`.
@@ -386,3 +412,50 @@ await tracker.disconnect(); // closes the WebSocket opened by start()
386
412
  ```
387
413
 
388
414
  Credentials and the trade account are fetched automatically on `connect`; Hyperliquid and Lighter account identifiers come from `connection.account.exchangeIdentifier`.
415
+
416
+ Pear returns the public trader identity only while its owner enables `sharingEnabled` in
417
+ trade account settings. Disabling it hides future Pear reads; the exchange public APIs
418
+ remain address-based and independent of Pear consent.
419
+
420
+ ## Hyperliquid subaccount funding
421
+
422
+ ```typescript
423
+ import { createHyperliquidHelper } from '@pear-protocol/exchanges-sdk';
424
+
425
+ const helper = createHyperliquidHelper();
426
+ await helper.transferSubAccount(ownerWallet, {
427
+ subAccountAddress: '0x...',
428
+ direction: 'main-to-subaccount', // use 'subaccount-to-main' to return USDC
429
+ amount: '1.25',
430
+ });
431
+ ```
432
+
433
+ Supply the owning main account's wallet (a viem wallet client/account or an ethers signer) in both
434
+ directions. Every transfer uses owner-signed `sendAsset` with `sourceDex: "spot"` and
435
+ `destinationDex: "spot"`. An approved API agent cannot sign this action. Browser wallet clients use
436
+ their connected chain for EIP-712 signing.
437
+
438
+ For standard accounts, this moves the separate spot USDC balance and does not fund perpetual
439
+ collateral. Unified and portfolio-margin accounts use their shared spot balance. The helper checks
440
+ subaccount ownership before signing. It does not read or change either account's mode.
441
+
442
+ A newly created standard subaccount receives USDC in spot and cannot use that balance as perpetual
443
+ margin. Before trading perps, make a separate owner-signed
444
+ [`usdClassTransfer`](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#transfer-from-spot-account-to-perp-account-and-vice-versa)
445
+ with `toPerp: true` and `subaccount:<address>` appended to the amount. The funding helper does not
446
+ submit this second action. A standard main account with USDC in perps must first move the intended
447
+ amount to spot before calling this helper.
448
+
449
+ The helper uses a fixed USDC token ID for the selected network without a metadata lookup, and the
450
+ amount stays an exact decimal string. Each successful transfer makes two information reads: the
451
+ signer's role and the subaccount's role and ownership. It then submits one transfer action.
452
+
453
+ Amounts must be positive decimal strings with at most six fractional digits and must fit the
454
+ existing safe integer micro-USDC limit (`9007199254.740991` USDC). The helper never rounds an amount.
455
+
456
+ Failures reach the caller without retries or fallback actions. After a timeout or connection loss,
457
+ check transfer history and balances before making another call; submission may have succeeded.
458
+
459
+ The routing and signing follow Hyperliquid's [Send Asset API](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#send-asset),
460
+ [account modes](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/account-abstraction-modes),
461
+ and [official signing implementation](https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/master/hyperliquid/utils/signing.py).
@@ -221,6 +221,17 @@ async function fetchAccountMetadata(accountIndex, authToken, connector, demo = f
221
221
  }
222
222
  }
223
223
  __name(fetchAccountMetadata, "fetchAccountMetadata");
224
+ async function fetchAccountMetadataByL1Address(l1Address, authToken, demo = false, connector = "lighter") {
225
+ const response = await proxyFetch(`${getLighterBaseUrl(connector, demo)}/api/v1/accountMetadata?by=l1_address&value=${encodeURIComponent(l1Address)}`, {
226
+ headers: {
227
+ accept: "application/json",
228
+ authorization: authToken
229
+ }
230
+ });
231
+ const data = await readLighterResponse(response, "/accountMetadata");
232
+ return data.account_metadatas ?? [];
233
+ }
234
+ __name(fetchAccountMetadataByL1Address, "fetchAccountMetadataByL1Address");
224
235
  async function fetchOrderBookDetails(authToken, connector, demo = false) {
225
236
  const response = await authenticatedLighterFetch(`${getLighterBaseUrl(connector, demo)}/api/v1/orderBookDetails`, authToken);
226
237
  const data = await readLighterResponse(response, "/orderBookDetails");
@@ -525,4 +536,4 @@ function readReferralAmount(value) {
525
536
  }
526
537
  __name(readReferralAmount, "readReferralAmount");
527
538
 
528
- export { LighterRequestError, createDepositIntentAddress, fetchAccount, fetchAccountLimits, fetchAccountMetadata, fetchAccountsByL1Address, fetchApiKeys, fetchAssetDetails, fetchDepositHistory, fetchDepositNetworks, fetchOrderBookDetails, fetchPositionFundingSince, fetchPublicAccount, fetchPublicOrderBookDetails, fetchTradesSince, fetchUserReferrals, fetchWithdrawHistory, submitReferralCode };
539
+ export { LighterRequestError, createDepositIntentAddress, fetchAccount, fetchAccountLimits, fetchAccountMetadata, fetchAccountMetadataByL1Address, fetchAccountsByL1Address, fetchApiKeys, fetchAssetDetails, fetchDepositHistory, fetchDepositNetworks, fetchOrderBookDetails, fetchPositionFundingSince, fetchPublicAccount, fetchPublicOrderBookDetails, fetchTradesSince, fetchUserReferrals, fetchWithdrawHistory, submitReferralCode };
@@ -1,7 +1,16 @@
1
+ import { HttpTransport } from '@nktkas/hyperliquid';
2
+ import { sendAsset, createSubAccount } from '@nktkas/hyperliquid/api/exchange';
3
+ import { getWalletAddress } from '@nktkas/hyperliquid/signing';
4
+ import { formatUnits } from 'viem';
1
5
  import { fetchInfo, fetchUserRole } from '../../client/hyperliquid/rest';
6
+ import { parseCollateralAmount } from '../subaccount.helper';
2
7
 
3
8
  var __defProp = Object.defineProperty;
4
9
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ const USDC_TOKENS = {
11
+ mainnet: "USDC:0x6d1e7cde53ba9467b783cb7c530ce054",
12
+ testnet: "USDC:0xeb62eee3685fc4c43992febcd9e75443"
13
+ };
5
14
  function createHyperliquidHelper(demo = false) {
6
15
  return {
7
16
  /**
@@ -14,6 +23,64 @@ function createHyperliquidHelper(demo = false) {
14
23
  const role = await fetchUserRole(userAddress, demo);
15
24
  return role.role !== "missing";
16
25
  },
26
+ /**
27
+ * Creates a named subaccount under the signer's main account. This is an L1 action pinned to
28
+ * chainId 1337, which browser wallets reject, so the signer is normally the approved agent key.
29
+ */
30
+ async createSubAccount(wallet, params) {
31
+ const transport = new HttpTransport({
32
+ isTestnet: demo
33
+ });
34
+ const response = await createSubAccount({
35
+ transport,
36
+ wallet
37
+ }, params);
38
+ return {
39
+ subAccountAddress: response.response.data
40
+ };
41
+ },
42
+ /** Moves spot USDC between owned accounts using the owning main wallet. */
43
+ async transferSubAccount(wallet, params) {
44
+ const usd = parseCollateralAmount(params.amount, "USDC");
45
+ const ownerAddress = await getWalletAddress(wallet);
46
+ const signerRole = await fetchUserRole(ownerAddress, demo);
47
+ if (signerRole.role === "agent") {
48
+ throw new Error("Spot subaccount transfers require the owner wallet; an approved agent cannot sign");
49
+ }
50
+ if (signerRole.role !== "user") {
51
+ throw new Error("Spot subaccount transfers require the owner wallet");
52
+ }
53
+ const subAccountRole = await fetchUserRole(params.subAccountAddress, demo);
54
+ if (subAccountRole.role !== "subAccount" || subAccountRole.data.master.toLowerCase() !== ownerAddress.toLowerCase()) {
55
+ throw new Error("Subaccount does not belong to the signing main account");
56
+ }
57
+ const isDeposit = isMainToSubAccount(params.direction);
58
+ const transport = new HttpTransport({
59
+ isTestnet: demo
60
+ });
61
+ await sendAsset({
62
+ transport,
63
+ wallet
64
+ }, {
65
+ destination: isDeposit ? params.subAccountAddress : ownerAddress,
66
+ sourceDex: "spot",
67
+ destinationDex: "spot",
68
+ token: demo ? USDC_TOKENS.testnet : USDC_TOKENS.mainnet,
69
+ amount: formatUnits(BigInt(usd), 6),
70
+ fromSubAccount: isDeposit ? "" : params.subAccountAddress
71
+ });
72
+ },
73
+ /** Lists a main account's existing subaccounts for transfer-destination selection. */
74
+ async listSubAccounts(userAddress) {
75
+ const subAccounts = await fetchInfo({
76
+ type: "subAccounts",
77
+ user: userAddress
78
+ }, demo);
79
+ return (subAccounts ?? []).map((subAccount) => ({
80
+ name: subAccount.name,
81
+ subAccountUser: subAccount.subAccountUser
82
+ }));
83
+ },
17
84
  /** Checks ownership and expiry for named and unnamed API wallets. */
18
85
  async checkApiWallet(params) {
19
86
  const agents = await fetchInfo({
@@ -46,5 +113,14 @@ function createHyperliquidHelper(demo = false) {
46
113
  };
47
114
  }
48
115
  __name(createHyperliquidHelper, "createHyperliquidHelper");
116
+ function isMainToSubAccount(direction) {
117
+ switch (direction) {
118
+ case "main-to-subaccount":
119
+ return true;
120
+ case "subaccount-to-main":
121
+ return false;
122
+ }
123
+ }
124
+ __name(isMainToSubAccount, "isMainToSubAccount");
49
125
 
50
126
  export { createHyperliquidHelper };
@@ -1,3 +1,5 @@
1
1
  export * from './hyperliquid';
2
2
  export * from './lighter';
3
+ export * from './subaccount.helper';
4
+ export * from './subaccount.types';
3
5
  export * from './types';
@@ -1,5 +1,5 @@
1
1
  import { isAddress, parseUnits } from 'viem';
2
- import { fetchApiKeys, fetchAccount, fetchPublicAccount, fetchAccountsByL1Address } from '../../client/lighter/rest';
2
+ import { fetchApiKeys, fetchAccount, fetchPublicAccount, fetchAccountMetadataByL1Address } from '../../client/lighter/rest';
3
3
  import { LIGHTER_QUOTE_ASSETS } from '../../tracker/lighter/const';
4
4
  import { parseCollateralAmount } from '../subaccount.helper';
5
5
  import { resolveAssetDecimals } from './asset-decimals.helper';
@@ -8,9 +8,10 @@ import { createDepositAddressForRoute, depositThroughRoute } from './deposit.hel
8
8
  import { writeWithdraw } from './ethereum';
9
9
  import { readWithdrawHistory, readDepositHistory } from './history.helper';
10
10
  import { selectLighterMiscRoutes } from './misc-routes.helper';
11
- import { LIGHTER_ACCOUNT_NOT_REGISTERED_CODE, readOnboardingStatus } from './onboarding.helper';
11
+ import { readOnboardingStatus } from './onboarding.helper';
12
12
  import { readUserReferrals, applyReferralCode } from './referral.helper';
13
13
  import { depositUsdgOnRobinhoodChain, createRhcDepositAddress } from './rhc-deposit.helper';
14
+ import { lookupLighterAccount, transferLighterSubAccount, setLighterSubAccountMetadata, createNamedLighterSubAccount, createLighterSubAccount } from './subaccount.helper';
14
15
  import { MIN_LIGHTER_FAST_WITHDRAW_COLLATERAL } from './types';
15
16
 
16
17
  var __defProp = Object.defineProperty;
@@ -84,6 +85,40 @@ function buildSharedLighterHelper(sdk, connector, demo) {
84
85
  const collateralSymbol = LIGHTER_QUOTE_ASSETS[connector];
85
86
  const minFastWithdraw = MIN_LIGHTER_FAST_WITHDRAW_COLLATERAL[connector];
86
87
  return {
88
+ /** Fetches full account metadata across an L1 address's account family, keyed by account index. */
89
+ async fetchAccountMetadata(params) {
90
+ if (demo) {
91
+ throw new Error("Lighter demo account metadata is unavailable because backend signing is mainnet-only");
92
+ }
93
+ const { token } = await misc.createAuthToken({
94
+ accountIndex: params.accountIndex,
95
+ apiKeyIndex: params.apiKeyIndex,
96
+ apiPrivateKey: params.apiPrivateKey
97
+ });
98
+ const metadatas = await fetchAccountMetadataByL1Address(params.l1Address, token, demo, connector);
99
+ return {
100
+ accounts: Object.fromEntries(metadatas.map((metadata) => [
101
+ metadata.account_index,
102
+ metadata
103
+ ]))
104
+ };
105
+ },
106
+ /** Creates a Lighter subaccount from the main account's API credentials. */
107
+ async createSubAccount(params) {
108
+ return createLighterSubAccount(sdk, params, connector, demo);
109
+ },
110
+ /** Creates, discovers, and names a Lighter subaccount before reporting completion. */
111
+ async createNamedSubAccount(params) {
112
+ return createNamedLighterSubAccount(sdk, params, connector, demo);
113
+ },
114
+ /** Sets a Lighter subaccount's display name and description. */
115
+ async setSubAccountMetadata(params) {
116
+ return setLighterSubAccountMetadata(sdk, params, connector, demo);
117
+ },
118
+ /** Moves perpetual collateral between a Lighter main account and one of its subaccounts. */
119
+ async transferSubAccount(params) {
120
+ return transferLighterSubAccount(sdk, params, connector, demo);
121
+ },
87
122
  /** Reports whether an L1 address still needs its first deposit before it owns a Lighter account. */
88
123
  async checkOnboarding(l1Address) {
89
124
  if (!isAddress(l1Address)) {
@@ -91,20 +126,9 @@ function buildSharedLighterHelper(sdk, connector, demo) {
91
126
  }
92
127
  return readOnboardingStatus(l1Address, connector, demo);
93
128
  },
129
+ /** Reads the selected deployment's account family. */
94
130
  async lookupAccount(l1Address) {
95
- const data = await fetchAccountsByL1Address(l1Address, connector, demo);
96
- if (data.code === LIGHTER_ACCOUNT_NOT_REGISTERED_CODE) {
97
- throw new Error("Account not registered on Lighter");
98
- }
99
- const accounts = data.sub_accounts ?? [];
100
- const main = accounts.find((s) => s.account_type === 0);
101
- if (!main) {
102
- throw new Error("Main account not found for this address");
103
- }
104
- return {
105
- mainAccountIndex: main.index,
106
- subAccountIndexes: accounts.filter((s) => s.account_type !== 0).map((s) => s.index)
107
- };
131
+ return lookupLighterAccount(l1Address, connector, demo);
108
132
  },
109
133
  async checkIntegratorApproval(accountIndex, integratorAccountIndex, requirements, authToken) {
110
134
  if (requirements) validateApprovalRequirements(requirements);
@@ -0,0 +1,118 @@
1
+ import { fetchAccountsByL1Address } from '../../client/lighter/rest';
2
+ import { LIGHTER_QUOTE_ASSETS } from '../../tracker/lighter/const';
3
+ import { parseCollateralAmount } from '../subaccount.helper';
4
+ import { selectLighterMiscRoutes } from './misc-routes.helper';
5
+ import { LIGHTER_ACCOUNT_NOT_REGISTERED_CODE } from './onboarding.helper';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ const NEW_SUBACCOUNT_POLL_INTERVAL_MS = 1500;
10
+ const NEW_SUBACCOUNT_POLL_ATTEMPTS = 8;
11
+ async function createNamedLighterSubAccount(sdk, params, connector, demo) {
12
+ if (demo) {
13
+ throw new Error("Lighter demo subaccount creation is unavailable because backend signing is mainnet-only");
14
+ }
15
+ const family = await lookupLighterAccount(params.l1Address, connector, demo);
16
+ const knownIndexes = new Set(family.subAccountIndexes);
17
+ const result = await createLighterSubAccount(sdk, params, connector, demo);
18
+ const subAccountIndex = await resolveNewLighterSubAccountIndex(params.l1Address, knownIndexes, connector, demo);
19
+ await setLighterSubAccountMetadata(sdk, {
20
+ ...params,
21
+ subAccountIndex
22
+ }, connector, demo);
23
+ return {
24
+ ...result,
25
+ subAccountIndex
26
+ };
27
+ }
28
+ __name(createNamedLighterSubAccount, "createNamedLighterSubAccount");
29
+ async function createLighterSubAccount(sdk, params, connector, demo) {
30
+ if (demo) {
31
+ throw new Error("Lighter demo subaccount creation is unavailable because backend signing is mainnet-only");
32
+ }
33
+ return selectLighterMiscRoutes(sdk, connector).createSubAccount({
34
+ accountIndex: params.mainAccountIndex,
35
+ apiKeyIndex: params.apiKeyIndex,
36
+ apiPrivateKey: params.apiPrivateKey
37
+ });
38
+ }
39
+ __name(createLighterSubAccount, "createLighterSubAccount");
40
+ async function setLighterSubAccountMetadata(sdk, params, connector, demo) {
41
+ if (demo) {
42
+ throw new Error("Lighter demo subaccount metadata is unavailable because backend signing is mainnet-only");
43
+ }
44
+ await selectLighterMiscRoutes(sdk, connector).setSubAccountMetadata({
45
+ mainAccountIndex: params.mainAccountIndex,
46
+ subAccountIndex: params.subAccountIndex,
47
+ apiKeyIndex: params.apiKeyIndex,
48
+ apiPrivateKey: params.apiPrivateKey,
49
+ name: params.name,
50
+ description: params.description
51
+ });
52
+ }
53
+ __name(setLighterSubAccountMetadata, "setLighterSubAccountMetadata");
54
+ async function transferLighterSubAccount(sdk, params, connector, demo) {
55
+ if (demo) {
56
+ throw new Error("Lighter demo subaccount transfers are unavailable because backend signing is mainnet-only");
57
+ }
58
+ const accounts = resolveTransferAccounts(params.mainAccountIndex, params.subAccountIndex, params.direction);
59
+ const amount = parseCollateralAmount(params.amount, LIGHTER_QUOTE_ASSETS[connector]);
60
+ return selectLighterMiscRoutes(sdk, connector).transferSubAccount({
61
+ l1Address: params.l1Address,
62
+ fromAccountIndex: accounts.fromAccountIndex,
63
+ toAccountIndex: accounts.toAccountIndex,
64
+ amount,
65
+ apiKeyIndex: params.apiKeyIndex,
66
+ apiPrivateKey: params.apiPrivateKey
67
+ });
68
+ }
69
+ __name(transferLighterSubAccount, "transferLighterSubAccount");
70
+ async function lookupLighterAccount(l1Address, connector, demo) {
71
+ const data = await fetchAccountsByL1Address(l1Address, connector, demo);
72
+ if (data.code === LIGHTER_ACCOUNT_NOT_REGISTERED_CODE) {
73
+ throw new Error("Account not registered on Lighter");
74
+ }
75
+ const accounts = data.sub_accounts ?? [];
76
+ const main = accounts.find((account) => account.account_type === 0);
77
+ if (!main) {
78
+ throw new Error("Main account not found for this address");
79
+ }
80
+ return {
81
+ mainAccountIndex: main.index,
82
+ subAccountIndexes: accounts.filter((account) => account.account_type === 1).map((account) => account.index)
83
+ };
84
+ }
85
+ __name(lookupLighterAccount, "lookupLighterAccount");
86
+ function resolveTransferAccounts(mainAccountIndex, subAccountIndex, direction) {
87
+ switch (direction) {
88
+ case "main-to-subaccount":
89
+ return {
90
+ fromAccountIndex: mainAccountIndex,
91
+ toAccountIndex: subAccountIndex
92
+ };
93
+ case "subaccount-to-main":
94
+ return {
95
+ fromAccountIndex: subAccountIndex,
96
+ toAccountIndex: mainAccountIndex
97
+ };
98
+ }
99
+ }
100
+ __name(resolveTransferAccounts, "resolveTransferAccounts");
101
+ async function resolveNewLighterSubAccountIndex(l1Address, knownIndexes, connector, demo) {
102
+ for (let attempt = 0; attempt < NEW_SUBACCOUNT_POLL_ATTEMPTS; attempt += 1) {
103
+ const lookup = await lookupLighterAccount(l1Address, connector, demo);
104
+ const newIndexes = lookup.subAccountIndexes.filter((index) => !knownIndexes.has(index));
105
+ if (newIndexes.length > 1) {
106
+ throw new Error("Subaccount created, but multiple new accounts appeared. Refresh and set its name manually.");
107
+ }
108
+ const [newIndex] = newIndexes;
109
+ if (newIndex !== void 0) {
110
+ return newIndex;
111
+ }
112
+ await new Promise((resolve) => setTimeout(resolve, NEW_SUBACCOUNT_POLL_INTERVAL_MS));
113
+ }
114
+ throw new Error("Subaccount created, but its index is not visible yet. Refresh and set its name shortly.");
115
+ }
116
+ __name(resolveNewLighterSubAccountIndex, "resolveNewLighterSubAccountIndex");
117
+
118
+ export { createLighterSubAccount, createNamedLighterSubAccount, lookupLighterAccount, setLighterSubAccountMetadata, transferLighterSubAccount };
@@ -0,0 +1 @@
1
+