@oydual31/more-vaults-sdk 0.2.7 → 0.2.9

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": "@oydual31/more-vaults-sdk",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "TypeScript SDK for MoreVaults protocol — viem/wagmi and ethers.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -89,13 +89,26 @@ export const OFT_ROUTES = {
89
89
  [10 /* optimism */]: { oft: '0x19cFCE47eD54a88614648DC3f19A5980097007dD' as `0x${string}`, token: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58' as `0x${string}` },
90
90
  },
91
91
  /**
92
- * PYUSDPayPal USD OFT (issued by PayPal/Flow).
92
+ * USDF — USD Flow OFT. Bridges PYUSD (Ethereum) USDF (Flow EVM).
93
+ * On Ethereum: underlying is PayPal USD (PYUSD, 0x6c3ea9...).
94
+ * On Flow: the OFT itself IS the token (USDF, 0x2aabea...).
93
95
  * Routes verified: Eth→Flow ✓
94
96
  */
95
- PYUSD: {
97
+ USDF: {
96
98
  [747 /* flowEVMMainnet */]: { oft: '0x2aabea2058b5ac2d339b163c6ab6f2b6d53aabed' as `0x${string}`, token: '0x2aabea2058b5ac2d339b163c6ab6f2b6d53aabed' as `0x${string}` },
97
99
  [1 /* ethereum */]: { oft: '0xfa0e06b54986ad96de87a8c56fea76fbd8d493f8' as `0x${string}`, token: '0x6c3ea9036406852006290770BEdFcAbA0e23A0e8' as `0x${string}` },
98
100
  },
101
+ /**
102
+ * PYUSD — PayPal USD bridged via OFTAdapter (Paxos / LayerZero).
103
+ * Lock/mint architecture: locks PYUSD on Arbitrum, mints PYUSD0 on Flow EVM.
104
+ * On Arbitrum: OFTAdapter wraps native PYUSD (0x46850a...).
105
+ * On Flow: OFTAdapter wraps PYUSD0 (0x99aF3E...), which is the native Paxos token.
106
+ * Routes verified: Arb↔Flow ✓ (EID 30336). No Eth or Base peers.
107
+ */
108
+ PYUSD: {
109
+ [747 /* flowEVMMainnet */]: { oft: '0x26d27d5AF2F6f1c14F40013C8619d97aaf015509' as `0x${string}`, token: '0x99aF3EeA856556646C98c8B9b2548Fe815240750' as `0x${string}` },
110
+ [42161 /* arbitrum */]: { oft: '0x3CD2b89C49D130C08f1d683225b2e5DeB63ff876' as `0x${string}`, token: '0x46850aD61C2B7d64d08c9C754F45254596696984' as `0x${string}` },
111
+ },
99
112
  /**
100
113
  * WFLOW — Wrapped FLOW NativeOFTAdapter (issued by Flow Foundation).
101
114
  * Routes verified: Eth→Flow ✓
@@ -217,7 +230,7 @@ export const OFT_ROUTES = {
217
230
  /**
218
231
  * oftCmd for Stargate v2 taxi mode (immediate per-message delivery).
219
232
  * Pass as `oftCmd` in SendParam when using stgUSDC, USDT, or WETH OFT_ROUTES entries.
220
- * Non-Stargate OFTs (PYUSD, WFLOW, sUSDe, USDe, weETH, rsETH) use empty bytes — pass `'0x'` instead.
233
+ * Non-Stargate OFTs (USDF, WFLOW, sUSDe, USDe, weETH, rsETH) use empty bytes — pass `'0x'` instead.
221
234
  */
222
235
  export const STARGATE_TAXI_CMD = '0x01' as const
223
236
 
package/src/viem/index.ts CHANGED
@@ -71,6 +71,7 @@ export {
71
71
  redeemAsync,
72
72
  smartRedeem,
73
73
  bridgeSharesToHub,
74
+ quoteShareBridgeFee,
74
75
  bridgeAssetsToSpoke,
75
76
  resolveRedeemAddresses,
76
77
  } from './redeemFlows'
@@ -8,7 +8,7 @@ import {
8
8
  pad,
9
9
  zeroAddress,
10
10
  } from 'viem'
11
- import { VAULT_ABI, BRIDGE_ABI, OFT_ABI, CONFIG_ABI, ERC20_ABI } from './abis'
11
+ import { VAULT_ABI, BRIDGE_ABI, OFT_ABI, CONFIG_ABI, ERC20_ABI, METADATA_ABI } from './abis'
12
12
  import type {
13
13
  VaultAddresses,
14
14
  RedeemResult,
@@ -354,6 +354,50 @@ export async function smartRedeem(
354
354
  return redeemShares(walletClient, publicClient, addresses, shares, receiver, owner)
355
355
  }
356
356
 
357
+ /**
358
+ * Quote the LZ fee for bridging shares from spoke to hub via SHARE_OFT.
359
+ *
360
+ * **IMPORTANT**: `amountLD` must be in SHARE_OFT native decimals (e.g. 18),
361
+ * NOT vault decimals (e.g. 8). Use the raw `SHARE_OFT.balanceOf(user)` value,
362
+ * or `getUserPositionMultiChain().rawSpokeShares[chainId]`.
363
+ *
364
+ * @param spokePublicClient Public client on the SPOKE chain
365
+ * @param shareOFT SHARE_OFT address on the spoke chain
366
+ * @param hubChainEid LayerZero Endpoint ID for the hub chain
367
+ * @param amountLD Shares in SHARE_OFT native decimals (raw balanceOf)
368
+ * @param receiver Receiver address on the hub chain
369
+ * @returns LZ native fee in wei
370
+ */
371
+ export async function quoteShareBridgeFee(
372
+ spokePublicClient: PublicClient,
373
+ shareOFT: Address,
374
+ hubChainEid: number,
375
+ amountLD: bigint,
376
+ receiver: Address,
377
+ ): Promise<bigint> {
378
+ const oft = getAddress(shareOFT)
379
+ const toBytes32 = pad(getAddress(receiver), { size: 32 })
380
+
381
+ const sendParam = {
382
+ dstEid: hubChainEid,
383
+ to: toBytes32,
384
+ amountLD,
385
+ minAmountLD: amountLD,
386
+ extraOptions: '0x' as `0x${string}`,
387
+ composeMsg: '0x' as `0x${string}`,
388
+ oftCmd: '0x' as `0x${string}`,
389
+ }
390
+
391
+ const feeResult = await spokePublicClient.readContract({
392
+ address: oft,
393
+ abi: OFT_ABI,
394
+ functionName: 'quoteSend',
395
+ args: [sendParam, false],
396
+ }) as { nativeFee: bigint; lzTokenFee: bigint }
397
+
398
+ return feeResult.nativeFee
399
+ }
400
+
357
401
  /**
358
402
  * R6 — Bridge shares from spoke to hub chain via OFT.
359
403
  *
@@ -362,24 +406,20 @@ export async function smartRedeem(
362
406
  * 2. `smartRedeem()` — redeem shares on hub → underlying (auto-detects async)
363
407
  * 3. `bridgeAssetsToSpoke()` — bridge assets from hub → spoke via asset OFT
364
408
  *
365
- * The steps happen on different chains and cannot be combined.
366
- * The frontend must switch chains between steps.
409
+ * **IMPORTANT**: The `shares` parameter must be in SHARE_OFT decimals (the raw
410
+ * `balanceOf` from the spoke OFT), NOT in vault decimals. Use the user's actual
411
+ * SHARE_OFT balance, or convert with `vaultShares * 10^(oftDecimals - vaultDecimals)`.
367
412
  *
368
413
  * **User transactions on spoke chain**: 1 approve (shares to shareOFT) + 1 OFT.send().
369
414
  * **Gas**: Requires native token on spoke for LZ fees, and gas on hub for steps 2+3.
370
415
  *
371
- * ## Tested flows
372
- *
373
- * - [x] SHARE_OFT bridge (Eth→Base, vault 0x8f74...ba6):
374
- * Delivery ~7 min. Required enforcedOptions on Eth SHARE_OFT for dstEid=30184.
375
- *
376
416
  * @param walletClient Wallet client on the SPOKE chain
377
417
  * @param publicClient Public client on the SPOKE chain
378
418
  * @param shareOFT OFTAdapter address for vault shares on the spoke chain
379
419
  * @param hubChainEid LayerZero Endpoint ID for the hub chain
380
- * @param shares Amount of vault shares to bridge
420
+ * @param shares Amount of shares in SHARE_OFT decimals (use raw balanceOf)
381
421
  * @param receiver Receiver address on the HUB chain
382
- * @param lzFee msg.value for OFT send (quote via OFT.quoteSend)
422
+ * @param lzFee msg.value for OFT send (from quoteShareBridgeFee)
383
423
  * @returns Transaction hash of the OFT.send() call
384
424
  */
385
425
  export async function bridgeSharesToHub(
@@ -527,9 +527,12 @@ const COMPOSER_SHARE_OFT_ABI = [
527
527
  export interface MultiChainUserPosition {
528
528
  /** Shares held directly on the hub vault (vault.balanceOf) */
529
529
  hubShares: bigint
530
- /** Per-spoke SHARE_OFT balances: { [chainId]: bigint } */
530
+ /** Per-spoke SHARE_OFT balances normalized to vault decimals: { [chainId]: bigint } */
531
531
  spokeShares: Record<number, bigint>
532
- /** hubShares + sum of all spokeShares */
532
+ /** Per-spoke SHARE_OFT raw balances in OFT native decimals: { [chainId]: bigint }
533
+ * Use these for bridgeSharesToHub() and quoteShareBridgeFee() */
534
+ rawSpokeShares: Record<number, bigint>
535
+ /** hubShares + sum of all spokeShares (in vault decimals) */
533
536
  totalShares: bigint
534
537
  /** convertToAssets(totalShares) on the hub */
535
538
  estimatedAssets: bigint
@@ -584,6 +587,7 @@ export async function getUserPositionMultiChain(
584
587
 
585
588
  // Step 3: resolve SHARE_OFT addresses for spokes (if any)
586
589
  const spokeShares: Record<number, bigint> = {}
590
+ const rawSpokeShares: Record<number, bigint> = {}
587
591
 
588
592
  if (topo.spokeChainIds.length > 0) {
589
593
  // Get hub SHARE_OFT via factory → composer → SHARE_OFT
@@ -610,7 +614,7 @@ export async function getUserPositionMultiChain(
610
614
  const spokePromises = topo.spokeChainIds.map(async (spokeChainId) => {
611
615
  try {
612
616
  const spokeEid = CHAIN_ID_TO_EID[spokeChainId]
613
- if (!spokeEid) return { chainId: spokeChainId, balance: 0n }
617
+ if (!spokeEid) return { chainId: spokeChainId, balance: 0n, rawBalance: 0n }
614
618
 
615
619
  // Get spoke SHARE_OFT address from hub peers()
616
620
  const spokeOftBytes32 = await (hubClient as PublicClient).readContract({
@@ -622,12 +626,12 @@ export async function getUserPositionMultiChain(
622
626
 
623
627
  const spokeOft = getAddress(`0x${spokeOftBytes32.slice(-40)}`) as Address
624
628
  if (spokeOft === '0x0000000000000000000000000000000000000000') {
625
- return { chainId: spokeChainId, balance: 0n }
629
+ return { chainId: spokeChainId, balance: 0n, rawBalance: 0n }
626
630
  }
627
631
 
628
632
  // Read balance + decimals on spoke chain
629
633
  const spokeClient = createChainClient(spokeChainId)
630
- if (!spokeClient) return { chainId: spokeChainId, balance: 0n }
634
+ if (!spokeClient) return { chainId: spokeChainId, balance: 0n, rawBalance: 0n }
631
635
 
632
636
  const [rawBalance, spokeOftDecimals] = await (spokeClient as PublicClient).multicall({
633
637
  contracts: [
@@ -648,15 +652,16 @@ export async function getUserPositionMultiChain(
648
652
  balance = rawBalance
649
653
  }
650
654
 
651
- return { chainId: spokeChainId, balance }
655
+ return { chainId: spokeChainId, balance, rawBalance }
652
656
  } catch {
653
- return { chainId: spokeChainId, balance: 0n }
657
+ return { chainId: spokeChainId, balance: 0n, rawBalance: 0n }
654
658
  }
655
659
  })
656
660
 
657
661
  const results = await Promise.all(spokePromises)
658
- for (const { chainId, balance } of results) {
662
+ for (const { chainId, balance, rawBalance } of results) {
659
663
  spokeShares[chainId] = balance
664
+ rawSpokeShares[chainId] = rawBalance
660
665
  }
661
666
  }
662
667
  }
@@ -686,6 +691,7 @@ export async function getUserPositionMultiChain(
686
691
  return {
687
692
  hubShares,
688
693
  spokeShares,
694
+ rawSpokeShares,
689
695
  totalShares,
690
696
  estimatedAssets,
691
697
  sharePrice,