@ember-finance/sdk 2.10.3 → 2.11.0-ccip.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.
Files changed (42) hide show
  1. package/dist/src/abis/AtomicLiquidity.json +36 -46
  2. package/dist/src/abis/CcipRouter.json +58 -0
  3. package/dist/src/abis/index.d.ts +3 -1
  4. package/dist/src/abis/index.js +4 -1
  5. package/dist/src/api/v2/apis/vaults-api.d.ts +42 -0
  6. package/dist/src/api/v2/apis/vaults-api.js +87 -0
  7. package/dist/src/api/v2/models/chain.d.ts +6 -0
  8. package/dist/src/api/v2/models/coin-placement.d.ts +113 -0
  9. package/dist/src/api/v2/models/coin-placement.js +17 -0
  10. package/dist/src/api/v2/models/coin-placements-response.d.ts +25 -0
  11. package/dist/src/api/v2/models/coin-placements-response.js +14 -0
  12. package/dist/src/api/v2/models/evm-ccip-lane.d.ts +73 -0
  13. package/dist/src/api/v2/models/evm-ccip-lane.js +14 -0
  14. package/dist/src/api/v2/models/evm-ccip-rate-limits.d.ts +36 -0
  15. package/dist/src/api/v2/models/evm-ccip-rate-limits.js +14 -0
  16. package/dist/src/api/v2/models/evm-ccip-token-pool.d.ts +122 -0
  17. package/dist/src/api/v2/models/evm-ccip-token-pool.js +17 -0
  18. package/dist/src/api/v2/models/evm-contracts.d.ts +9 -0
  19. package/dist/src/api/v2/models/evm-oftadapter.d.ts +11 -0
  20. package/dist/src/api/v2/models/evm-oftadapter.js +4 -1
  21. package/dist/src/api/v2/models/evm-oftpeer.d.ts +12 -0
  22. package/dist/src/api/v2/models/index.d.ts +6 -0
  23. package/dist/src/api/v2/models/index.js +6 -0
  24. package/dist/src/api/v2/models/instant-withdraw-config.d.ts +21 -2
  25. package/dist/src/api/v2/models/poc-report-protocol-asset.d.ts +60 -0
  26. package/dist/src/api/v2/models/poc-report-protocol-asset.js +18 -0
  27. package/dist/src/api/v2/models/poc-report-protocol.d.ts +13 -0
  28. package/dist/src/api/v2/models/transparency-row.d.ts +1 -1
  29. package/dist/src/evm-vaults/interfaces/index.d.ts +39 -0
  30. package/dist/src/evm-vaults/on-chain-calls/tx-builder.d.ts +17 -11
  31. package/dist/src/evm-vaults/on-chain-calls/tx-builder.js +39 -12
  32. package/dist/src/evm-vaults/on-chain-calls/user.d.ts +35 -31
  33. package/dist/src/evm-vaults/on-chain-calls/user.js +69 -43
  34. package/dist/src/evm-vaults/on-chain-calls/vault-reader.d.ts +14 -1
  35. package/dist/src/evm-vaults/on-chain-calls/vault-reader.js +30 -0
  36. package/dist/src/evm-vaults/utils/ccip.d.ts +23 -0
  37. package/dist/src/evm-vaults/utils/ccip.js +32 -0
  38. package/dist/src/evm-vaults/utils/deployment-parser.d.ts +27 -1
  39. package/dist/src/evm-vaults/utils/deployment-parser.js +37 -0
  40. package/dist/src/evm-vaults/utils/index.d.ts +1 -0
  41. package/dist/src/evm-vaults/utils/index.js +1 -0
  42. package/package.json +2 -6
@@ -1,6 +1,6 @@
1
1
  import { Signer, Provider } from "ethers";
2
2
  import { EVMOnChainCalls } from "./onchain-calls.js";
3
- import { EvmProtocolInfo, IUserCallOptions, IEvmCallOptions, ICoverVaultCallOptions, EvmOnChainCallResponse, IPermitSignature, IETHDepositOptions, IBridgeSendOptions, IOFTFeeQuote } from "../interfaces/index.js";
3
+ import { EvmProtocolInfo, IUserCallOptions, IEvmCallOptions, ICoverVaultCallOptions, EvmOnChainCallResponse, IPermitSignature, IETHDepositOptions, IBridgeSendOptions, IOFTFeeQuote, ICcipFeeQuote, ICcipBridgeSendOptions } from "../interfaces/index.js";
4
4
  import { NumStr } from "../../common/types.js";
5
5
  import { Chain } from "../../api/v2/index.js";
6
6
  /**
@@ -423,6 +423,38 @@ export declare class EVMUserCalls extends EVMOnChainCalls {
423
423
  * @returns Transaction execution response
424
424
  */
425
425
  bridgeSendToken(destinationChain: Chain, tokenAddress: string, amount: NumStr, receiver: string, feeQuote: IOFTFeeQuote, options?: IBridgeSendOptions): Promise<EvmOnChainCallResponse>;
426
+ /**
427
+ * Bridges a receipt token to a destination chain using the vault's CCIP pool.
428
+ *
429
+ * The counterpart to bridgeSendToken. Call getBridgeTransportForVault first
430
+ * and pick the matching method — a vault has exactly one active bridge, and
431
+ * calling the wrong one throws rather than silently doing nothing.
432
+ *
433
+ * Two differences from the LayerZero path worth knowing:
434
+ * - the allowance goes to the CCIP Router, not to the pool, because the
435
+ * Router is what pulls the tokens;
436
+ * - the destination is named by network, and the chain selector is read
437
+ * from the configured lane. An unconfigured route throws here rather
438
+ * than reverting on chain.
439
+ *
440
+ * @param destinationNetwork Destination network name, e.g. "atlantic"
441
+ * @param tokenAddress Receipt token address (also the vault identifier in EVM deployment data)
442
+ * @param amount Amount to bridge in local token decimals
443
+ * @param receiver Recipient wallet address on the destination chain
444
+ * @param feeQuote Fee quote returned by reader.getCcipFeeQuote
445
+ * @param options Optional bridge/send overrides
446
+ * @returns Transaction execution response
447
+ */
448
+ bridgeSendTokenViaCcip(destinationNetwork: string, tokenAddress: string, amount: NumStr, receiver: string, feeQuote: ICcipFeeQuote, options?: ICcipBridgeSendOptions): Promise<EvmOnChainCallResponse>;
449
+ /**
450
+ * Reports which bridge a vault uses, or undefined if it has none.
451
+ *
452
+ * A vault has exactly one active bridge, but different vaults use different
453
+ * ones, so branch on this rather than assuming either transport.
454
+ *
455
+ * @param tokenAddress Receipt token address (also the vault identifier in EVM deployment data)
456
+ */
457
+ getBridgeTransportForVault(tokenAddress: string): "LayerZero" | "CCIP" | undefined;
426
458
  /**
427
459
  * Gets the vault contract instance for read operations
428
460
  * @param vaultAddress The address of the vault
@@ -477,38 +509,10 @@ export declare class EVMUserCalls extends EVMOnChainCalls {
477
509
  * that can neither receive ETH nor call claimWithdrawalTo. Owner-only.
478
510
  */
479
511
  recoverUnclaimedWithdrawal(vaultAddress: string, receiver: string, to: string, options?: IEvmCallOptions): Promise<EvmOnChainCallResponse>;
480
- /**
481
- * Gas floor for atomic-vault user paths that may hit `_ensureLiquidity` —
482
- * i.e. call into the configured ERC-4626 strategy's `withdraw` to top up the
483
- * vault's idle collateral before paying the caller. A strategy `withdraw`
484
- * routinely lands in the 200-400k band on its own; combined with the vault-
485
- * side accounting an OOG revert at 500k is easy to trip (observed on
486
- * mainnet: tx 0x93bd9f36…3d8ef34409, 482k / 500k budget, empty revert data).
487
- *
488
- * Ethers' auto-estimation can also under-budget when a Timelock proposal
489
- * freezes the gas limit at scheduling time and the strategy's on-chain
490
- * state drifts before execution. Enforce a conservative floor here so
491
- * callers don't need to know the mechanics; explicit `options.gasLimit`
492
- * still overrides.
493
- */
494
- private static readonly ATOMIC_STRATEGY_PULL_GAS_FLOOR;
495
- /** Applies the atomic-vault gas floor unless the caller has already set one. */
496
- private withAtomicGasFloor;
497
512
  /** Atomic vault: instant (capped, fee'd) withdraw of `shares` to `receiver`; auto-pulls from strategy. */
498
513
  instantWithdraw(vaultAddress: string, shares: NumStr, receiver: string, options?: IEvmCallOptions): Promise<EvmOnChainCallResponse>;
499
- /**
500
- * Atomic vault: swap `shareAmount` of a whitelisted source EmberShare `share` into collateral.
501
- *
502
- * `minNetOut` is the audit-fix (A-9) slippage floor: reverts with
503
- * `SlippageToleranceExceeded` if the computed net payout falls below it.
504
- * Pass `0` to disable the guard; otherwise call
505
- * {@link EVMReaderCalls.previewSwapInstantWithdraw} first and derive
506
- * `minNetOut` from `quote.netAmount` minus your tolerance.
507
- *
508
- * A 2M gas floor is applied when the caller doesn't set `options.gasLimit`
509
- * — see {@link ATOMIC_STRATEGY_PULL_GAS_FLOOR} for the reasoning.
510
- */
511
- swapEmberShares(vaultAddress: string, share: string, shareAmount: NumStr, receiver: string, minNetOut: NumStr, options?: IEvmCallOptions): Promise<EvmOnChainCallResponse>;
514
+ /** Atomic vault: swap `shareAmount` of a whitelisted source EmberShare `share` into collateral. */
515
+ swapEmberShares(vaultAddress: string, share: string, shareAmount: NumStr, receiver: string, options?: IEvmCallOptions): Promise<EvmOnChainCallResponse>;
512
516
  /**
513
517
  * Atomic vault: claim your own escrowed holdback — collateral that couldn't be
514
518
  * delivered during `releaseUsersHoldback` (e.g. you were blocklisted on the
@@ -637,6 +637,67 @@ export class EVMUserCalls extends EVMOnChainCalls {
637
637
  }, refundAddress);
638
638
  return this.execCall(txCall, options);
639
639
  }
640
+ /**
641
+ * Bridges a receipt token to a destination chain using the vault's CCIP pool.
642
+ *
643
+ * The counterpart to bridgeSendToken. Call getBridgeTransportForVault first
644
+ * and pick the matching method — a vault has exactly one active bridge, and
645
+ * calling the wrong one throws rather than silently doing nothing.
646
+ *
647
+ * Two differences from the LayerZero path worth knowing:
648
+ * - the allowance goes to the CCIP Router, not to the pool, because the
649
+ * Router is what pulls the tokens;
650
+ * - the destination is named by network, and the chain selector is read
651
+ * from the configured lane. An unconfigured route throws here rather
652
+ * than reverting on chain.
653
+ *
654
+ * @param destinationNetwork Destination network name, e.g. "atlantic"
655
+ * @param tokenAddress Receipt token address (also the vault identifier in EVM deployment data)
656
+ * @param amount Amount to bridge in local token decimals
657
+ * @param receiver Recipient wallet address on the destination chain
658
+ * @param feeQuote Fee quote returned by reader.getCcipFeeQuote
659
+ * @param options Optional bridge/send overrides
660
+ * @returns Transaction execution response
661
+ */
662
+ async bridgeSendTokenViaCcip(destinationNetwork, tokenAddress, amount, receiver, feeQuote, options) {
663
+ if (!this.provider) {
664
+ throw new Error("Provider required for bridge send read operations and allowance checks");
665
+ }
666
+ const pool = this.parser.getCcipTokenPoolByVaultAddress(tokenAddress);
667
+ if (!pool?.router) {
668
+ throw new Error(`CCIP token pool not found for token address: ${tokenAddress}`);
669
+ }
670
+ const lane = this.parser.getCcipLaneByVaultAndNetwork(tokenAddress, destinationNetwork);
671
+ if (!lane?.remoteChainSelector) {
672
+ throw new Error(`No CCIP lane configured from this chain to "${destinationNetwork}" for token ${tokenAddress}`);
673
+ }
674
+ const amountBigInt = BigInt(amount);
675
+ // The Router moves the tokens, so it is the Router that needs approval.
676
+ const token = new ethers.Contract(tokenAddress, ["function allowance(address,address) view returns (uint256)"], this.provider);
677
+ const currentAllowance = await token.allowance(await this.getWalletAddress(), pool.router);
678
+ if (currentAllowance < amountBigInt) {
679
+ throw new Error(`Insufficient allowance for bridge transfer. Current allowance: ${currentAllowance}, required: ${amountBigInt}`);
680
+ }
681
+ const txCall = this.txBuilder.ccipSend(pool.router, {
682
+ destinationChainSelector: lane.remoteChainSelector,
683
+ receiver,
684
+ amount: amountBigInt,
685
+ feeToken: options?.feeToken,
686
+ destinationGasLimit: options?.destinationGasLimit
687
+ }, tokenAddress, feeQuote.fee);
688
+ return this.execCall(txCall, options);
689
+ }
690
+ /**
691
+ * Reports which bridge a vault uses, or undefined if it has none.
692
+ *
693
+ * A vault has exactly one active bridge, but different vaults use different
694
+ * ones, so branch on this rather than assuming either transport.
695
+ *
696
+ * @param tokenAddress Receipt token address (also the vault identifier in EVM deployment data)
697
+ */
698
+ getBridgeTransportForVault(tokenAddress) {
699
+ return this.parser.getBridgeTransportByVaultAddress(tokenAddress);
700
+ }
640
701
  // ============================================
641
702
  // Read-Only Methods
642
703
  // ============================================
@@ -732,35 +793,18 @@ export class EVMUserCalls extends EVMOnChainCalls {
732
793
  async recoverUnclaimedWithdrawal(vaultAddress, receiver, to, options) {
733
794
  return this.execCall(this.txBuilder.recoverUnclaimedWithdrawal(vaultAddress, receiver, to), options);
734
795
  }
735
- /** Applies the atomic-vault gas floor unless the caller has already set one. */
736
- withAtomicGasFloor(options) {
737
- if (options?.gasLimit && options.gasLimit > 0n)
738
- return options;
739
- return {
740
- ...(options ?? {}),
741
- gasLimit: EVMUserCalls.ATOMIC_STRATEGY_PULL_GAS_FLOOR
742
- };
743
- }
796
+ // ============================================
797
+ // Atomic Liquidity Vault — user actions (BFE-817/818)
798
+ // ============================================
744
799
  /** Atomic vault: instant (capped, fee'd) withdraw of `shares` to `receiver`; auto-pulls from strategy. */
745
800
  async instantWithdraw(vaultAddress, shares, receiver, options) {
746
801
  const txCall = this.txBuilder.atomicInstantWithdraw(vaultAddress, shares, receiver);
747
- return this.execCall(txCall, this.withAtomicGasFloor(options));
802
+ return this.execCall(txCall, options);
748
803
  }
749
- /**
750
- * Atomic vault: swap `shareAmount` of a whitelisted source EmberShare `share` into collateral.
751
- *
752
- * `minNetOut` is the audit-fix (A-9) slippage floor: reverts with
753
- * `SlippageToleranceExceeded` if the computed net payout falls below it.
754
- * Pass `0` to disable the guard; otherwise call
755
- * {@link EVMReaderCalls.previewSwapInstantWithdraw} first and derive
756
- * `minNetOut` from `quote.netAmount` minus your tolerance.
757
- *
758
- * A 2M gas floor is applied when the caller doesn't set `options.gasLimit`
759
- * — see {@link ATOMIC_STRATEGY_PULL_GAS_FLOOR} for the reasoning.
760
- */
761
- async swapEmberShares(vaultAddress, share, shareAmount, receiver, minNetOut, options) {
762
- const txCall = this.txBuilder.atomicSwapEmberShares(vaultAddress, share, shareAmount, receiver, minNetOut);
763
- return this.execCall(txCall, this.withAtomicGasFloor(options));
804
+ /** Atomic vault: swap `shareAmount` of a whitelisted source EmberShare `share` into collateral. */
805
+ async swapEmberShares(vaultAddress, share, shareAmount, receiver, options) {
806
+ const txCall = this.txBuilder.atomicSwapEmberShares(vaultAddress, share, shareAmount, receiver);
807
+ return this.execCall(txCall, options);
764
808
  }
765
809
  /**
766
810
  * Atomic vault: claim your own escrowed holdback — collateral that couldn't be
@@ -795,21 +839,3 @@ export class EVMUserCalls extends EVMOnChainCalls {
795
839
  return this.execCall(this.txBuilder.atomicRecoverUnclaimedHoldback(vaultAddress, recipient, to), options);
796
840
  }
797
841
  }
798
- // ============================================
799
- // Atomic Liquidity Vault — user actions (BFE-817/818)
800
- // ============================================
801
- /**
802
- * Gas floor for atomic-vault user paths that may hit `_ensureLiquidity` —
803
- * i.e. call into the configured ERC-4626 strategy's `withdraw` to top up the
804
- * vault's idle collateral before paying the caller. A strategy `withdraw`
805
- * routinely lands in the 200-400k band on its own; combined with the vault-
806
- * side accounting an OOG revert at 500k is easy to trip (observed on
807
- * mainnet: tx 0x93bd9f36…3d8ef34409, 482k / 500k budget, empty revert data).
808
- *
809
- * Ethers' auto-estimation can also under-budget when a Timelock proposal
810
- * freezes the gas limit at scheduling time and the strategy's on-chain
811
- * state drifts before execution. Enforce a conservative floor here so
812
- * callers don't need to know the mechanics; explicit `options.gasLimit`
813
- * still overrides.
814
- */
815
- EVMUserCalls.ATOMIC_STRATEGY_PULL_GAS_FLOOR = 2000000n;
@@ -1,6 +1,6 @@
1
1
  import { Provider } from "ethers";
2
2
  import { EVMDeploymentParser } from "../utils/deployment-parser.js";
3
- import { EvmProtocolInfo, IOFTSendParam, IOFTFeeQuote, IOFTVaultSupportedChain, IParkingLotInfo } from "../interfaces/index.js";
3
+ import { EvmProtocolInfo, IOFTSendParam, IOFTFeeQuote, IOFTVaultSupportedChain, IParkingLotInfo, ICcipSendParam, ICcipFeeQuote } from "../interfaces/index.js";
4
4
  import { NumStr } from "../../common/types.js";
5
5
  import { Chain, VaultsApi } from "../../api/v2/index.js";
6
6
  /**
@@ -524,4 +524,17 @@ export declare class EVMVaultReader {
524
524
  * @returns Combined fee quote including native fee and expected amounts
525
525
  */
526
526
  getOFTFeeQuote(vaultId: string, sendParam: IOFTSendParam, payInLzToken?: boolean): Promise<IOFTFeeQuote>;
527
+ /**
528
+ * Quotes what a CCIP bridge send will cost.
529
+ *
530
+ * The counterpart to getOFTFeeQuote. CCIP charges one fee rather than
531
+ * LayerZero's native/LZ-token pair, and quotes no received amount: a
532
+ * burn-and-mint pool mints exactly what it burned, so the amount received is
533
+ * the amount sent.
534
+ *
535
+ * @param vaultId The vault (receipt token) address on the source chain
536
+ * @param sendParam Destination, receiver and amount
537
+ * @returns The fee and the token it is charged in
538
+ */
539
+ getCcipFeeQuote(vaultId: string, sendParam: ICcipSendParam): Promise<ICcipFeeQuote>;
527
540
  }
@@ -4,6 +4,8 @@ import EmberVaultABI from "../../abis/EmberVault.json" with { type: "json" };
4
4
  import EmberETHVaultABI from "../../abis/EmberETHVault.json" with { type: "json" };
5
5
  import EmberProtocolConfigABI from "../../abis/EmberProtocolConfig.json" with { type: "json" };
6
6
  import ERC20TokenABI from "../../abis/ERC20Token.json" with { type: "json" };
7
+ import CcipRouterABI from "../../abis/CcipRouter.json" with { type: "json" };
8
+ import { ZERO_ADDRESS, encodeCcipReceiver, encodeCcipExtraArgs } from "../utils/ccip.js";
7
9
  import EmberVaultOFTAdapterABI from "../../abis/EmberVaultMintBurnOFTAdapter.json" with { type: "json" };
8
10
  import EmberDepositParkingLotABI from "../../abis/EmberDepositParkingLot.json" with { type: "json" };
9
11
  import { Configuration, VaultsApi } from "../../api/v2/index.js";
@@ -734,4 +736,32 @@ export class EVMVaultReader {
734
736
  maxAmountLD: oftQuote.oftLimit.maxAmountLD
735
737
  };
736
738
  }
739
+ /**
740
+ * Quotes what a CCIP bridge send will cost.
741
+ *
742
+ * The counterpart to getOFTFeeQuote. CCIP charges one fee rather than
743
+ * LayerZero's native/LZ-token pair, and quotes no received amount: a
744
+ * burn-and-mint pool mints exactly what it burned, so the amount received is
745
+ * the amount sent.
746
+ *
747
+ * @param vaultId The vault (receipt token) address on the source chain
748
+ * @param sendParam Destination, receiver and amount
749
+ * @returns The fee and the token it is charged in
750
+ */
751
+ async getCcipFeeQuote(vaultId, sendParam) {
752
+ const pool = this.parser.getCcipTokenPoolByVaultAddress(vaultId);
753
+ if (!pool?.router) {
754
+ throw new Error(`CCIP token pool not found for vault: ${vaultId}`);
755
+ }
756
+ const feeToken = sendParam.feeToken ?? ZERO_ADDRESS;
757
+ const router = new Contract(pool.router, CcipRouterABI.abi, this.provider);
758
+ const fee = await router.getFee(sendParam.destinationChainSelector, {
759
+ receiver: encodeCcipReceiver(sendParam.receiver),
760
+ data: "0x",
761
+ tokenAmounts: [{ token: vaultId, amount: sendParam.amount }],
762
+ feeToken,
763
+ extraArgs: encodeCcipExtraArgs(sendParam.destinationGasLimit)
764
+ });
765
+ return { fee, feeToken };
766
+ }
737
767
  }
@@ -0,0 +1,23 @@
1
+ /** The native currency, as CCIP names it when no fee token is given. */
2
+ export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
3
+ /**
4
+ * Encodes a receiver for a CCIP message.
5
+ *
6
+ * CCIP carries the receiver as `bytes` rather than `address`, because the far
7
+ * end of a lane need not be an EVM chain. On an EVM destination that is the
8
+ * address left-padded to 32 bytes, which is why the same account looks
9
+ * different on the two sides of a transfer.
10
+ */
11
+ export declare const encodeCcipReceiver: (receiver: string) => string;
12
+ /**
13
+ * Encodes CCIP extraArgs.
14
+ *
15
+ * `allowOutOfOrderExecution` is true because a token transfer carries no
16
+ * message the destination has to apply in sequence; requiring ordering would
17
+ * make one stuck message block every later one on the lane.
18
+ *
19
+ * gasLimit is 0 by default: a plain token transfer runs no code on the
20
+ * destination beyond the pool's own mint, and paying for gas that is never used
21
+ * only raises the fee.
22
+ */
23
+ export declare const encodeCcipExtraArgs: (destinationGasLimit?: bigint) => string;
@@ -0,0 +1,32 @@
1
+ import { AbiCoder, getAddress, zeroPadValue } from "ethers";
2
+ /** The native currency, as CCIP names it when no fee token is given. */
3
+ export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
4
+ /**
5
+ * Tag for CCIP's EVMExtraArgsV2, the first four bytes of
6
+ * keccak256("CCIP EVMExtraArgsV2"). Verified against a deployed ramp.
7
+ */
8
+ const EVM_EXTRA_ARGS_V2_TAG = "0x181dcf10";
9
+ /**
10
+ * Encodes a receiver for a CCIP message.
11
+ *
12
+ * CCIP carries the receiver as `bytes` rather than `address`, because the far
13
+ * end of a lane need not be an EVM chain. On an EVM destination that is the
14
+ * address left-padded to 32 bytes, which is why the same account looks
15
+ * different on the two sides of a transfer.
16
+ */
17
+ export const encodeCcipReceiver = (receiver) => zeroPadValue(getAddress(receiver), 32);
18
+ /**
19
+ * Encodes CCIP extraArgs.
20
+ *
21
+ * `allowOutOfOrderExecution` is true because a token transfer carries no
22
+ * message the destination has to apply in sequence; requiring ordering would
23
+ * make one stuck message block every later one on the lane.
24
+ *
25
+ * gasLimit is 0 by default: a plain token transfer runs no code on the
26
+ * destination beyond the pool's own mint, and paying for gas that is never used
27
+ * only raises the fee.
28
+ */
29
+ export const encodeCcipExtraArgs = (destinationGasLimit = 0n) => {
30
+ const encoded = AbiCoder.defaultAbiCoder().encode(["uint256", "bool"], [destinationGasLimit, true]);
31
+ return EVM_EXTRA_ARGS_V2_TAG + encoded.slice(2);
32
+ };
@@ -1,4 +1,4 @@
1
- import { EvmOFTAdapter } from "../../api/v2/index.js";
1
+ import { EvmCcipLane, EvmCcipTokenPool, EvmOFTAdapter } from "../../api/v2/index.js";
2
2
  import { EvmProtocolInfo } from "../interfaces/index.js";
3
3
  export declare class EVMDeploymentParser {
4
4
  deployment: EvmProtocolInfo;
@@ -124,4 +124,30 @@ export declare class EVMDeploymentParser {
124
124
  * @param vaultAddress The vault address (receipt token address in EVM deployment data)
125
125
  */
126
126
  getOFTAdapterObjectByVaultAddress(vaultAddress: string): EvmOFTAdapter | undefined;
127
+ /**
128
+ * Finds the CCIP token pool entry for a given vault address.
129
+ * @param vaultAddress The vault address (receipt token address in EVM deployment data)
130
+ */
131
+ getCcipTokenPoolByVaultAddress(vaultAddress: string): EvmCcipTokenPool | undefined;
132
+ /**
133
+ * Reports which bridge a vault uses, or undefined if it has none.
134
+ *
135
+ * A vault has exactly one active bridge, but different vaults use different
136
+ * ones, so call this before choosing between the LayerZero and CCIP send
137
+ * methods rather than assuming either.
138
+ *
139
+ * @param vaultAddress The vault address (receipt token address in EVM deployment data)
140
+ */
141
+ getBridgeTransportByVaultAddress(vaultAddress: string): "LayerZero" | "CCIP" | undefined;
142
+ /**
143
+ * Finds the configured CCIP lane from a vault to a destination network.
144
+ *
145
+ * Lanes are keyed by the destination's CCIP chain selector, which nothing
146
+ * outside CCIP knows, so this looks the lane up by network name instead.
147
+ * A missing lane means the route is not configured — sending would revert.
148
+ *
149
+ * @param vaultAddress The vault address on the source chain
150
+ * @param destinationNetwork The destination network name, e.g. "atlantic"
151
+ */
152
+ getCcipLaneByVaultAndNetwork(vaultAddress: string, destinationNetwork: string): EvmCcipLane | undefined;
127
153
  }
@@ -206,4 +206,41 @@ export class EVMDeploymentParser {
206
206
  }
207
207
  return entry;
208
208
  }
209
+ /**
210
+ * Finds the CCIP token pool entry for a given vault address.
211
+ * @param vaultAddress The vault address (receipt token address in EVM deployment data)
212
+ */
213
+ getCcipTokenPoolByVaultAddress(vaultAddress) {
214
+ return Object.values(this.deployment.contracts?.ccipTokenPools ?? {}).find(pool => pool.vaultAddress?.toLowerCase() === vaultAddress.toLowerCase());
215
+ }
216
+ /**
217
+ * Reports which bridge a vault uses, or undefined if it has none.
218
+ *
219
+ * A vault has exactly one active bridge, but different vaults use different
220
+ * ones, so call this before choosing between the LayerZero and CCIP send
221
+ * methods rather than assuming either.
222
+ *
223
+ * @param vaultAddress The vault address (receipt token address in EVM deployment data)
224
+ */
225
+ getBridgeTransportByVaultAddress(vaultAddress) {
226
+ if (this.getCcipTokenPoolByVaultAddress(vaultAddress))
227
+ return "CCIP";
228
+ if (this.getOFTAdapterObjectByVaultAddress(vaultAddress))
229
+ return "LayerZero";
230
+ return undefined;
231
+ }
232
+ /**
233
+ * Finds the configured CCIP lane from a vault to a destination network.
234
+ *
235
+ * Lanes are keyed by the destination's CCIP chain selector, which nothing
236
+ * outside CCIP knows, so this looks the lane up by network name instead.
237
+ * A missing lane means the route is not configured — sending would revert.
238
+ *
239
+ * @param vaultAddress The vault address on the source chain
240
+ * @param destinationNetwork The destination network name, e.g. "atlantic"
241
+ */
242
+ getCcipLaneByVaultAndNetwork(vaultAddress, destinationNetwork) {
243
+ const pool = this.getCcipTokenPoolByVaultAddress(vaultAddress);
244
+ return Object.values(pool?.lanes ?? {}).find(lane => lane.remoteNetwork?.toLowerCase() === destinationNetwork.toLowerCase());
245
+ }
209
246
  }
@@ -1,2 +1,3 @@
1
1
  export * from "./deployment-parser.js";
2
2
  export * from "./permit-utils.js";
3
+ export * from "./ccip.js";
@@ -1,2 +1,3 @@
1
1
  export * from "./deployment-parser.js";
2
2
  export * from "./permit-utils.js";
3
+ export * from "./ccip.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ember-finance/sdk",
3
3
  "description": "Ember Protocol SDK",
4
- "version": "2.10.3",
4
+ "version": "2.11.0-ccip.0",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
7
7
  "main": "./dist/index.js",
@@ -136,11 +136,7 @@
136
136
  "markdown-it": ">=12.3.2",
137
137
  "ajv": "^6.12.6",
138
138
  "jsonpointer": ">=5.0.0",
139
- "form-data": ">=3.0.4",
140
- "basic-ftp": "^5.3.1",
141
- "fast-xml-parser": "^5.3.5",
142
- "shell-quote": "^1.10.0",
143
- "tar": "^7.5.19"
139
+ "form-data": ">=3.0.4"
144
140
  },
145
141
  "packageManager": "yarn@4.14.1"
146
142
  }