@defisaver/ethena-sdk 0.0.4 → 0.0.6

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 (76) hide show
  1. package/cjs/config/contracts.d.ts +181 -0
  2. package/cjs/config/contracts.js +72 -0
  3. package/cjs/constants/index.d.ts +7 -0
  4. package/cjs/constants/index.js +10 -0
  5. package/cjs/contracts.d.ts +696 -0
  6. package/cjs/contracts.js +80 -0
  7. package/cjs/exchange/index.d.ts +10 -0
  8. package/cjs/exchange/index.js +185 -0
  9. package/cjs/execution/index.d.ts +2 -0
  10. package/cjs/execution/index.js +15 -0
  11. package/cjs/execution/morpho.d.ts +2 -0
  12. package/cjs/execution/morpho.js +56 -0
  13. package/cjs/index.d.ts +5 -1
  14. package/cjs/index.js +9 -1
  15. package/cjs/positionData/index.d.ts +1 -1
  16. package/cjs/positionData/index.js +2 -2
  17. package/cjs/positionData/morpho.d.ts +1 -1
  18. package/cjs/positionData/morpho.js +15 -7
  19. package/cjs/safe/index.d.ts +6 -0
  20. package/cjs/safe/index.js +80 -0
  21. package/cjs/services/viem.d.ts +31 -31
  22. package/cjs/types/common.d.ts +10 -0
  23. package/cjs/types/exchange.d.ts +19 -0
  24. package/cjs/types/exchange.js +12 -0
  25. package/cjs/types/execution.d.ts +9 -0
  26. package/cjs/types/execution.js +8 -0
  27. package/cjs/types/index.d.ts +3 -0
  28. package/cjs/types/index.js +3 -0
  29. package/cjs/types/safe.d.ts +5 -0
  30. package/cjs/types/safe.js +2 -0
  31. package/esm/config/contracts.d.ts +181 -0
  32. package/esm/config/contracts.js +69 -0
  33. package/esm/constants/index.d.ts +7 -0
  34. package/esm/constants/index.js +7 -0
  35. package/esm/contracts.d.ts +696 -0
  36. package/esm/contracts.js +37 -0
  37. package/esm/exchange/index.d.ts +10 -0
  38. package/esm/exchange/index.js +176 -0
  39. package/esm/execution/index.d.ts +2 -0
  40. package/esm/execution/index.js +11 -0
  41. package/esm/execution/morpho.d.ts +2 -0
  42. package/esm/execution/morpho.js +52 -0
  43. package/esm/index.d.ts +5 -1
  44. package/esm/index.js +5 -1
  45. package/esm/positionData/index.d.ts +1 -1
  46. package/esm/positionData/index.js +2 -2
  47. package/esm/positionData/morpho.d.ts +1 -1
  48. package/esm/positionData/morpho.js +16 -8
  49. package/esm/safe/index.d.ts +6 -0
  50. package/esm/safe/index.js +75 -0
  51. package/esm/services/viem.d.ts +31 -31
  52. package/esm/types/common.d.ts +10 -0
  53. package/esm/types/exchange.d.ts +19 -0
  54. package/esm/types/exchange.js +9 -0
  55. package/esm/types/execution.d.ts +9 -0
  56. package/esm/types/execution.js +5 -0
  57. package/esm/types/index.d.ts +3 -0
  58. package/esm/types/index.js +3 -0
  59. package/esm/types/safe.d.ts +5 -0
  60. package/esm/types/safe.js +1 -0
  61. package/package.json +3 -1
  62. package/src/config/contracts.ts +72 -0
  63. package/src/constants/index.ts +7 -0
  64. package/src/contracts.ts +57 -0
  65. package/src/exchange/index.ts +195 -0
  66. package/src/execution/index.ts +12 -0
  67. package/src/execution/morpho.ts +47 -0
  68. package/src/index.ts +8 -0
  69. package/src/positionData/index.ts +2 -2
  70. package/src/positionData/morpho.ts +17 -8
  71. package/src/safe/index.ts +99 -0
  72. package/src/types/common.ts +11 -0
  73. package/src/types/exchange.ts +21 -0
  74. package/src/types/execution.ts +11 -0
  75. package/src/types/index.ts +4 -1
  76. package/src/types/safe.ts +5 -0
@@ -0,0 +1,47 @@
1
+ import { NetworkNumber } from '@defisaver/positions-sdk';
2
+ import { MorphoManagerContract } from '../contracts';
3
+ import { getViemProvider } from '../services/viem';
4
+ import { Request, RequestType } from '../types';
5
+ import { predictSafeAddress } from '../safe';
6
+
7
+ const morphoAuthSignature: Request = {
8
+ type: RequestType.Signature,
9
+ getParams: async (rpcUrl: string, network: NetworkNumber, userAddress: string) => {
10
+ const provider = getViemProvider(rpcUrl, network);
11
+ const managerContract = MorphoManagerContract(provider, network);
12
+ const nonce = await managerContract.read.nonce([userAddress as `0x${string}`]);
13
+ const tenMinutes = 1000 * 60 * 10;
14
+ const deadline = Date.now() + tenMinutes;
15
+ const safeAddress = await predictSafeAddress(userAddress, rpcUrl, network);
16
+
17
+ return {
18
+ types: {
19
+ EIP712Domain: [
20
+ { name: 'verifyingContract', type: 'address' },
21
+ { name: 'chainId', type: 'uint256' },
22
+ ],
23
+ Authorization: [
24
+ { name: 'authorizer', type: 'address' },
25
+ { name: 'authorized', type: 'address' },
26
+ { name: 'isAuthorized', type: 'bool' },
27
+ { name: 'nonce', type: 'uint256' },
28
+ { name: 'deadline', type: 'uint256' },
29
+ ],
30
+ },
31
+ domain: {
32
+ chainId: network,
33
+ verifyingContract: managerContract.address,
34
+ },
35
+ primaryType: 'Authorization',
36
+ message: {
37
+ authorizer: userAddress,
38
+ authorized: safeAddress,
39
+ isAuthorized: true,
40
+ nonce: +nonce.toString(),
41
+ deadline,
42
+ },
43
+ };
44
+ },
45
+ };
46
+
47
+ export const getMorphoRequests = () => [morphoAuthSignature];
package/src/index.ts CHANGED
@@ -2,10 +2,18 @@ import './setup';
2
2
 
3
3
  import * as positionData from './positionData';
4
4
  import * as marketData from './marketData';
5
+ import * as exchange from './exchange';
6
+ import * as constants from './constants';
7
+ import * as safe from './safe';
8
+ import * as execution from './execution';
5
9
 
6
10
  export * from './types';
7
11
 
8
12
  export {
9
13
  positionData,
10
14
  marketData,
15
+ exchange,
16
+ constants,
17
+ safe,
18
+ execution,
11
19
  };
@@ -13,10 +13,10 @@ export const getMaxLeverageForSupplyAmount = (marketData: MarketData, supplyAmou
13
13
  }
14
14
  };
15
15
 
16
- export const getResultingPosition = async (marketData: MarketData, supplyAmount: string, leverage: number, rpcUrl: string, network: NetworkNumber): Promise<PositionData> => {
16
+ export const getResultingPosition = async (marketData: MarketData, supplyAmount: string, leverage: number, userAddress: string, rpcUrl: string, network: NetworkNumber): Promise<PositionData> => {
17
17
  switch (marketData.market) {
18
18
  case SupportedMarkets.MorphoBlueSUSDeUSDtb_915: {
19
- return getMorphoResultingPosition(marketData, supplyAmount, leverage, rpcUrl, network);
19
+ return getMorphoResultingPosition(marketData, supplyAmount, leverage, userAddress, rpcUrl, network);
20
20
  }
21
21
  default:
22
22
  throw new Error(`Unsupported market: ${marketData.market}`);
@@ -1,10 +1,11 @@
1
1
  import Dec from 'decimal.js';
2
- import { getAssetInfo } from '@defisaver/tokens';
2
+ import { assetAmountInWei, getAssetInfo } from '@defisaver/tokens';
3
3
  import {
4
4
  helpers, markets, MMUsedAssets, morphoBlue, MorphoBlueVersions, NetworkNumber,
5
5
  } from '@defisaver/positions-sdk';
6
6
  import { AssetData, MarketData, PositionData } from '../types';
7
7
  import { getViemProvider } from '../services/viem';
8
+ import { getBestPrice } from '../exchange';
8
9
 
9
10
  const getMaxBoostUsd = (lltv: string, borrowLimit: string, debt: string, targetRatio = 1.01, bufferPercent = 1) => new Dec(targetRatio).mul(debt).sub(borrowLimit)
10
11
  .div(new Dec(lltv).sub(targetRatio).toString())
@@ -38,24 +39,24 @@ export const getMorphoMaxLeverageForSupplyAmount = (marketData: MarketData, supp
38
39
  return maxLeverage;
39
40
  };
40
41
 
41
- export const getMorphoResultingPosition = async (marketData: MarketData, supplyAmount: string, leverage: number, rpcUrl: string, network: NetworkNumber): Promise<PositionData> => {
42
+ export const getMorphoResultingPosition = async (marketData: MarketData, supplyAmount: string, leverage: number, userAddress: string, rpcUrl: string, network: NetworkNumber): Promise<PositionData> => {
42
43
  const provider = getViemProvider(rpcUrl, network);
43
44
 
44
45
  const morphoMarket = markets.MorphoBlueMarkets(network)[MorphoBlueVersions.MorphoBlueSUSDeUSDtb_915];
45
46
  const {
46
47
  rate: oracle, assetsData,
47
48
  } = marketData;
49
+ const supplyAsset: AssetData = Object.values(assetsData).find((asset) => !asset.isDebtAsset)!;
50
+ const borrowAsset: AssetData = Object.values(assetsData).find((asset) => asset.isDebtAsset)!;
48
51
  const debtAmount = new Dec(leverage)
49
52
  .times(supplyAmount).minus(supplyAmount).times(oracle)
50
53
  .toString();
51
54
 
52
- // TODO: add price fetching logic
53
- const priceForAmount = new Dec(1).div(oracle).toString();
54
- const leveragedAmount = new Dec(debtAmount).times(priceForAmount);
55
- const collIncrease = new Dec(supplyAmount).plus(leveragedAmount).toString();
55
+ const debtAmountWei = assetAmountInWei(debtAmount, borrowAsset.symbol);
56
+ const { priceWithFee, source } = await getBestPrice(borrowAsset.symbol, supplyAsset.symbol, debtAmountWei, userAddress, network);
56
57
 
57
- const supplyAsset: AssetData = Object.values(assetsData).find((asset) => !asset.isDebtAsset)!;
58
- const borrowAsset: AssetData = Object.values(assetsData).find((asset) => asset.isDebtAsset)!;
58
+ const leveragedAmount = new Dec(debtAmount).times(priceWithFee);
59
+ const collIncrease = new Dec(supplyAmount).plus(leveragedAmount).toString();
59
60
 
60
61
  const morphoMarketData = await morphoBlue._getMorphoBlueMarketData(provider, network, morphoMarket);
61
62
  const usedAssets: MMUsedAssets = {};
@@ -84,6 +85,14 @@ export const getMorphoResultingPosition = async (marketData: MarketData, supplyA
84
85
 
85
86
  const aggregatedPosition = helpers.morphoBlueHelpers.getMorphoBlueAggregatedPositionData({ usedAssets, assetsData: morphoMarketData.assetsData, marketInfo: morphoMarketData });
86
87
  return {
88
+ exchangeInfo: {
89
+ price: priceWithFee,
90
+ source,
91
+ sellAsset: borrowAsset.symbol,
92
+ sellAmount: debtAmount,
93
+ buyAsset: supplyAsset.symbol,
94
+ buyAmount: leveragedAmount.toString(),
95
+ },
87
96
  usedAssets,
88
97
  ...aggregatedPosition,
89
98
  };
@@ -0,0 +1,99 @@
1
+ import { NetworkNumber } from '@defisaver/positions-sdk';
2
+ import {
3
+ encodeFunctionData, encodePacked, getCreate2Address, keccak256,
4
+ } from 'viem';
5
+ import { Wallet } from '../types';
6
+ import {
7
+ SAFE_API_URL, SAFE_REFUND_RECEIVER, SALT_PREFIX, ZERO_ADDRESS,
8
+ } from '../constants';
9
+ import {
10
+ getConfigContractAbi,
11
+ getSafeFactoryAddress, getSafeFallbackHandlerAddress, getSafeWalletSingletonAddress, SafeFactoryContract,
12
+ } from '../contracts';
13
+ import { getViemProvider } from '../services/viem';
14
+
15
+ export const getSafeWallets = async (userAddress: string, network: NetworkNumber): Promise<{ success: boolean, wallets: string[] }> => {
16
+ try {
17
+ const res = await fetch(`${SAFE_API_URL}/safe/all-wallets?network=${network}&account=${userAddress}`);
18
+ const wallets = await res.json();
19
+
20
+ const oneOneWallets = wallets
21
+ .filter((wallet: Wallet) => wallet.type === 'Safe' && (wallet.owners || []).length === 1)
22
+ .map((wallet: Wallet) => wallet.address);
23
+ return { success: true, wallets: oneOneWallets };
24
+ } catch (e) {
25
+ return { success: false, wallets: [] };
26
+ }
27
+ };
28
+
29
+ const getSafeSetupParams = (owners: string[], threshold: number, network: NetworkNumber) => [
30
+ owners as `0x${string}`[],
31
+ BigInt(threshold),
32
+ ZERO_ADDRESS as `0x${string}`,
33
+ '0x',
34
+ getSafeFallbackHandlerAddress(network) as `0x${string}`,
35
+ ZERO_ADDRESS as `0x${string}`,
36
+ BigInt(0),
37
+ SAFE_REFUND_RECEIVER as `0x${string}`,
38
+ ];
39
+
40
+ const _predictSafeAddress = async (
41
+ rpcUrl: string,
42
+ network: NetworkNumber,
43
+ setupArgs: string,
44
+ saltNonce: string,
45
+ ) => {
46
+ const provider = getViemProvider(rpcUrl, network);
47
+ const safeProxyFactoryAddress = getSafeFactoryAddress(network);
48
+ const safeProxyFactoryContract = SafeFactoryContract(provider, network);
49
+ const masterCopyAddress = getSafeWalletSingletonAddress(network);
50
+
51
+ const proxyCreationCode = await safeProxyFactoryContract.read.proxyCreationCode();
52
+
53
+ // @ts-ignore
54
+ const initCodeHash = keccak256(
55
+ encodePacked(['bytes', 'bytes'], [proxyCreationCode, masterCopyAddress.slice(2).padStart(64, '0') as `0x${string}`]), 'bytes',
56
+ ) as string;
57
+
58
+ const salt = keccak256(
59
+ encodePacked(
60
+ ['bytes', 'uint256'],
61
+ [keccak256(setupArgs as `0x${string}`), BigInt(saltNonce)],
62
+ ),
63
+ );
64
+
65
+ return getCreate2Address(
66
+ {
67
+ bytecodeHash: initCodeHash as `0x${string}`,
68
+ from: safeProxyFactoryAddress,
69
+ salt,
70
+ },
71
+ );
72
+ };
73
+
74
+ export const predictSafeAddress = async (owner: string, rpcUrl: string, network: NetworkNumber): Promise<string> => {
75
+ const provider = getViemProvider(rpcUrl, network);
76
+
77
+ const threshold = 1;
78
+ const owners = [owner];
79
+ const setupParams = getSafeSetupParams(owners, threshold, network);
80
+ const setupParamsEncoded = encodeFunctionData({
81
+ abi: getConfigContractAbi('Safe130'),
82
+ functionName: 'setup',
83
+ // @ts-ignore
84
+ args: setupParams,
85
+ });
86
+ const oneOfOneWalletsCount = (await getSafeWallets(owner, network)).wallets.length;
87
+ const failAfter = 10;
88
+ for (let nonce = oneOfOneWalletsCount + 1; nonce < oneOfOneWalletsCount + failAfter + 1; nonce += 1) {
89
+ const salt = `${SALT_PREFIX}${nonce}`;
90
+ const predictedAddr = await _predictSafeAddress(rpcUrl, network, setupParamsEncoded, salt);
91
+ const bytecode = await provider.getCode({ address: predictedAddr });
92
+ if (!bytecode) {
93
+ // safe does not exist
94
+ return predictedAddr;
95
+ }
96
+ }
97
+
98
+ return '';
99
+ };
@@ -2,6 +2,7 @@ import {
2
2
  IncentiveData, MMUsedAssets, MorphoBlueAggregatedPositionData, NetworkNumber,
3
3
  } from '@defisaver/positions-sdk';
4
4
  import { SupportedMarkets } from './markets';
5
+ import { OffchainExchanges } from './exchange';
5
6
 
6
7
  export interface AssetData {
7
8
  symbol: string;
@@ -23,8 +24,18 @@ export interface MarketData {
23
24
  rate: string;
24
25
  }
25
26
 
27
+ export interface ExchangeInfo {
28
+ price: string;
29
+ source: OffchainExchanges | 'None';
30
+ sellAsset: string;
31
+ sellAmount: string;
32
+ buyAsset: string;
33
+ buyAmount: string;
34
+ }
35
+
26
36
  export interface PositionData extends MorphoBlueAggregatedPositionData {
27
37
  usedAssets: MMUsedAssets;
38
+ exchangeInfo: ExchangeInfo;
28
39
  }
29
40
 
30
41
  export {
@@ -0,0 +1,21 @@
1
+ export enum OffchainExchanges {
2
+ ZeroX = '0x',
3
+ Paraswap = 'Paraswap',
4
+ Kyberswap = 'Kyberswap',
5
+ OneInch = '1Inch',
6
+ // Odos = 'Odos',
7
+ Bebop = 'Bebop',
8
+ }
9
+
10
+ export interface PriceData {
11
+ price: string;
12
+ priceWithFee: string;
13
+ source: OffchainExchanges | 'None';
14
+ wrapper: string;
15
+ to: string;
16
+ allowanceTarget: string;
17
+ protocolFee: string;
18
+ data: string;
19
+ value: string;
20
+ gas: string;
21
+ }
@@ -0,0 +1,11 @@
1
+ import { NetworkNumber } from '@defisaver/positions-sdk';
2
+
3
+ export enum RequestType {
4
+ Signature = 'Signature',
5
+ EthCall = 'EthCall',
6
+ }
7
+
8
+ export interface Request {
9
+ type: RequestType;
10
+ getParams: (rpcUrl: string, network: NetworkNumber, userAddress: string) => Promise<any>;
11
+ }
@@ -1,2 +1,5 @@
1
1
  export * from './common';
2
- export * from './markets';
2
+ export * from './markets';
3
+ export * from './exchange';
4
+ export * from './safe';
5
+ export * from './execution';
@@ -0,0 +1,5 @@
1
+ export type Wallet = {
2
+ address: string;
3
+ type: string;
4
+ owners?: string[];
5
+ };