@dhedge/v2-sdk 1.10.15 → 1.10.16

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": "@dhedge/v2-sdk",
3
- "version": "1.10.15",
3
+ "version": "1.10.16",
4
4
  "license": "MIT",
5
5
  "description": "🛠 An SDK for building applications on top of dHEDGE V2",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,122 @@
1
+ [
2
+ {
3
+ "inputs": [
4
+ {
5
+ "internalType": "address",
6
+ "name": "receiver",
7
+ "type": "address"
8
+ },
9
+ {
10
+ "internalType": "address",
11
+ "name": "market",
12
+ "type": "address"
13
+ },
14
+ {
15
+ "internalType": "uint256",
16
+ "name": "netPtIn",
17
+ "type": "uint256"
18
+ },
19
+ {
20
+ "internalType": "uint256",
21
+ "name": "netLpIn",
22
+ "type": "uint256"
23
+ },
24
+ {
25
+ "components": [
26
+ {
27
+ "internalType": "address",
28
+ "name": "tokenOut",
29
+ "type": "address"
30
+ },
31
+ {
32
+ "internalType": "uint256",
33
+ "name": "minTokenOut",
34
+ "type": "uint256"
35
+ },
36
+ {
37
+ "internalType": "address",
38
+ "name": "tokenRedeemSy",
39
+ "type": "address"
40
+ },
41
+ {
42
+ "internalType": "address",
43
+ "name": "pendleSwap",
44
+ "type": "address"
45
+ },
46
+ {
47
+ "components": [
48
+ {
49
+ "internalType": "enum SwapType",
50
+ "name": "swapType",
51
+ "type": "uint8"
52
+ },
53
+ {
54
+ "internalType": "address",
55
+ "name": "extRouter",
56
+ "type": "address"
57
+ },
58
+ {
59
+ "internalType": "bytes",
60
+ "name": "extCalldata",
61
+ "type": "bytes"
62
+ },
63
+ {
64
+ "internalType": "bool",
65
+ "name": "needScale",
66
+ "type": "bool"
67
+ }
68
+ ],
69
+ "internalType": "struct SwapData",
70
+ "name": "swapData",
71
+ "type": "tuple"
72
+ }
73
+ ],
74
+ "internalType": "struct TokenOutput",
75
+ "name": "output",
76
+ "type": "tuple"
77
+ }
78
+ ],
79
+ "name": "exitPostExpToToken",
80
+ "outputs": [
81
+ {
82
+ "internalType": "uint256",
83
+ "name": "totalTokenOut",
84
+ "type": "uint256"
85
+ },
86
+ {
87
+ "components": [
88
+ {
89
+ "internalType": "uint256",
90
+ "name": "netPtFromRemove",
91
+ "type": "uint256"
92
+ },
93
+ {
94
+ "internalType": "uint256",
95
+ "name": "netSyFromRemove",
96
+ "type": "uint256"
97
+ },
98
+ {
99
+ "internalType": "uint256",
100
+ "name": "netPtRedeem",
101
+ "type": "uint256"
102
+ },
103
+ {
104
+ "internalType": "uint256",
105
+ "name": "netSyFromRedeem",
106
+ "type": "uint256"
107
+ },
108
+ {
109
+ "internalType": "uint256",
110
+ "name": "totalSyOut",
111
+ "type": "uint256"
112
+ }
113
+ ],
114
+ "internalType": "struct IPActionMiscV3.ExitPostExpReturnParams",
115
+ "name": "params",
116
+ "type": "tuple"
117
+ }
118
+ ],
119
+ "stateMutability": "nonpayable",
120
+ "type": "function"
121
+ }
122
+ ]
package/src/config.ts CHANGED
@@ -44,7 +44,8 @@ export const routerAddress: AddressDappNetworkMap = {
44
44
  [Dapp.VELODROMEV2]: "0xa062ae8a9c5e11aaa026fc2670b0d65ccc8b2858",
45
45
  [Dapp.LYRA]: "0xCCE7819d65f348c64B7Beb205BA367b3fE33763B",
46
46
  [Dapp.ARRAKIS]: "0x9ce88a56d120300061593eF7AD074A1B710094d5",
47
- [Dapp.ODOS]: "0xca423977156bb05b13a2ba3b76bc5419e2fe9680"
47
+ [Dapp.ODOS]: "0xca423977156bb05b13a2ba3b76bc5419e2fe9680",
48
+ [Dapp.PENDLE]: "0x888888888889758F76e7103c6CbF23ABbF58F946"
48
49
  },
49
50
  [Network.ARBITRUM]: {
50
51
  [Dapp.ONEINCH]: "0x111111125421ca6dc452d289314280a0f8842a65",
@@ -53,18 +54,21 @@ export const routerAddress: AddressDappNetworkMap = {
53
54
  [Dapp.BALANCER]: "0xBA12222222228d8Ba445958a75a0704d566BF2C8",
54
55
  [Dapp.RAMSES]: "0xaaa87963efeb6f7e0a2711f397663105acb1805e",
55
56
  [Dapp.TOROS]: "0xA5679C4272A056Bb83f039961fae7D99C48529F5",
56
- [Dapp.ODOS]: "0xa669e7A0d4b3e4Fa48af2dE86BD4CD7126Be4e13"
57
+ [Dapp.ODOS]: "0xa669e7A0d4b3e4Fa48af2dE86BD4CD7126Be4e13",
58
+ [Dapp.PENDLE]: "0x888888888889758F76e7103c6CbF23ABbF58F946"
57
59
  },
58
60
  [Network.BASE]: {
59
61
  [Dapp.ONEINCH]: "0x111111125421ca6dc452d289314280a0f8842a65",
60
62
  [Dapp.AERODROME]: "0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43",
61
63
  [Dapp.AAVEV3]: "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5",
62
64
  [Dapp.TOROS]: "0xf067575Eb60c7587C11e867907AA7284833704d1",
63
- [Dapp.ODOS]: "0x19cEeAd7105607Cd444F5ad10dd51356436095a1"
65
+ [Dapp.ODOS]: "0x19cEeAd7105607Cd444F5ad10dd51356436095a1",
66
+ [Dapp.PENDLE]: "0x888888888889758F76e7103c6CbF23ABbF58F946"
64
67
  },
65
68
  [Network.SONIC]: {
66
69
  [Dapp.AAVEV3]: "0x5362dBb1e601abF3a4c14c22ffEdA64042E5eAA3",
67
- [Dapp.ODOS]: "0xaC041Df48dF9791B0654f1Dbbf2CC8450C5f2e9D"
70
+ [Dapp.ODOS]: "0xaC041Df48dF9791B0654f1Dbbf2CC8450C5f2e9D",
71
+ [Dapp.PENDLE]: "0x888888888889758F76e7103c6CbF23ABbF58F946"
68
72
  }
69
73
  };
70
74
 
@@ -204,5 +208,12 @@ export const flatMoneyContractAddresses: Readonly<Partial<
204
208
  FlatcoinVault: "0x86C7b9640302082B0dF78023F930d8612bFcaD3f",
205
209
  COLLATERAL: "0x68f180fcCe6836688e9084f035309E29Bf0A2095", // WBTC
206
210
  StableModule: "0x357CB23571EF7a3d6189b7FAcFC361eA71f7CAB5"
211
+ },
212
+ [Network.ARBITRUM]: {
213
+ OrderExecution: "0x7e50AD6E467D9FAFC3B4BFd003247cEaA2F17e5b",
214
+ DelayedOrder: "0x2326BB21B769D81E134C9b305ca156f989249fE7", // OrderAnnouncementModule
215
+ FlatcoinVault: "0x29fAD9d44C550e5D8081AB35763797B39d75b858",
216
+ COLLATERAL: "0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f", // WBTC
217
+ StableModule: "0xcD3657cB0E851b6a734c4D1e7FC2640Bcd9f6B2d"
207
218
  }
208
219
  };
@@ -86,6 +86,7 @@ import {
86
86
  getPancakeUnStakeTxData,
87
87
  } from "../services/pancake/staking";
88
88
  import { getOdosSwapTxData } from "../services/odos";
89
+ import { getPendleSwapTxData } from "../services/pendle";
89
90
 
90
91
  export class Pool {
91
92
  public readonly poolLogic: Contract;
@@ -422,6 +423,15 @@ export class Pool {
422
423
  slippage
423
424
  );
424
425
  break;
426
+ case Dapp.PENDLE:
427
+ swapTxData = await getPendleSwapTxData(
428
+ this,
429
+ assetFrom,
430
+ assetTo,
431
+ amountIn,
432
+ slippage
433
+ );
434
+ break;
425
435
  default:
426
436
  const iUniswapV2Router = new ethers.utils.Interface(
427
437
  IUniswapV2Router.abi
@@ -0,0 +1,141 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import axios from "axios";
3
+ import { ApiError, ethers } from "../..";
4
+ import { networkChainIdMap } from "../../config";
5
+ import { Pool } from "../../entities";
6
+ import ActionMiscV3Abi from "../../abi/pendle/ActionMiscV3.json";
7
+
8
+ const pendleBaseUrl = "https://api-v2.pendle.finance/core/v1";
9
+
10
+ export async function getPendleSwapTxData(
11
+ pool: Pool,
12
+ tokenIn: string,
13
+ tokenOut: string,
14
+ amountIn: ethers.BigNumber | string,
15
+ slippage: number
16
+ ): Promise<string> {
17
+ const expiredMarket = await checkExitPostExpPT(pool, tokenIn, tokenOut);
18
+ if (expiredMarket) {
19
+ return getExitExpPTTxData(pool, tokenOut, amountIn, expiredMarket);
20
+ }
21
+ const params = {
22
+ receiver: pool.address,
23
+ tokenIn,
24
+ tokenOut,
25
+ amountIn: amountIn.toString(),
26
+ slippage
27
+ };
28
+ const market = await getMarket(pool, tokenIn, tokenOut);
29
+ try {
30
+ const swapResult = await axios.get(
31
+ `${pendleBaseUrl}/sdk/${
32
+ networkChainIdMap[pool.network]
33
+ }/markets/${market}/swap`,
34
+ { params }
35
+ );
36
+
37
+ return swapResult.data.tx.data;
38
+ } catch (e) {
39
+ console.error("Error in Pendle API request:", e);
40
+ throw new ApiError("Pendle api request failed");
41
+ }
42
+ }
43
+
44
+ const checkUnderlying = (market: any, token: string, networkId: number) => {
45
+ if (market.underlyingAsset !== `${networkId}-${token.toLocaleLowerCase()}`) {
46
+ throw new Error("Can only trade in or out of the underlying asset");
47
+ }
48
+ };
49
+
50
+ export async function getMarket(
51
+ pool: Pool,
52
+ tokenIn: string,
53
+ tokenOut: string
54
+ ): Promise<string> {
55
+ const networkId = networkChainIdMap[pool.network];
56
+ let marketResult;
57
+ try {
58
+ marketResult = await axios.get(
59
+ `${pendleBaseUrl}/${networkId}/markets/active`
60
+ );
61
+ } catch (e) {
62
+ console.error("Error in Pendle API request:", e);
63
+ throw new ApiError("Pendle api request failed");
64
+ }
65
+
66
+ const allMarkets = marketResult.data.markets;
67
+ const markets = [tokenIn, tokenOut].map(token =>
68
+ allMarkets.find(
69
+ (market: any) => market.pt === `${networkId}-${token.toLocaleLowerCase()}`
70
+ )
71
+ );
72
+ if (markets[0]) {
73
+ checkUnderlying(markets[0], tokenOut, networkId);
74
+ return markets[0].address;
75
+ } else if (markets[1]) {
76
+ checkUnderlying(markets[1], tokenIn, networkId);
77
+ return markets[1].address;
78
+ } else {
79
+ throw new Error("Can only trade PT assets");
80
+ }
81
+ }
82
+
83
+ const checkExitPostExpPT = async (
84
+ pool: Pool,
85
+ tokenIn: string,
86
+ tokenOut: string
87
+ ): Promise<string | null> => {
88
+ const networkId = networkChainIdMap[pool.network];
89
+ let inactiveMarketResult;
90
+ try {
91
+ inactiveMarketResult = await axios.get(
92
+ `${pendleBaseUrl}/${networkId}/markets/inactive`
93
+ );
94
+ } catch (e) {
95
+ console.error("Error in Pendle API request:", e);
96
+ throw new ApiError("Pendle api request failed");
97
+ }
98
+ const allInactiveMarkets = inactiveMarketResult.data.markets;
99
+ const markets = [tokenIn, tokenOut].map(token =>
100
+ allInactiveMarkets.find(
101
+ (market: any) => market.pt === `${networkId}-${token.toLocaleLowerCase()}`
102
+ )
103
+ );
104
+ if (markets[0]) {
105
+ checkUnderlying(markets[0], tokenOut, networkId);
106
+ return markets[0].address;
107
+ } else if (markets[1]) {
108
+ throw new Error("Can not trade to expired PT asset");
109
+ } else {
110
+ return null;
111
+ }
112
+ };
113
+
114
+ const getExitExpPTTxData = (
115
+ pool: Pool,
116
+ tokenOut: string,
117
+ amountIn: ethers.BigNumber | string,
118
+ market: string
119
+ ) => {
120
+ const actionMiscV3 = new ethers.utils.Interface(ActionMiscV3Abi);
121
+ const txData = actionMiscV3.encodeFunctionData("exitPostExpToToken", [
122
+ pool.address, // receiver
123
+ market, // market
124
+ amountIn.toString(), // netPtIn
125
+ 0, // netLpIn
126
+ [
127
+ tokenOut,
128
+ 0, // minTokenOut
129
+ tokenOut, // tokenRedeemSy,
130
+ ethers.constants.AddressZero, //pendleSwap;
131
+ // swapData
132
+ [
133
+ 0, // swapType
134
+ ethers.constants.AddressZero, // extRouter
135
+ ethers.constants.HashZero, // extCalldata
136
+ false // needScale
137
+ ]
138
+ ]
139
+ ]);
140
+ return txData;
141
+ };
@@ -0,0 +1,132 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ import { Dhedge, Pool } from "..";
4
+
5
+ import { Dapp, Network } from "../types";
6
+ import { CONTRACT_ADDRESS, MAX_AMOUNT } from "./constants";
7
+ import {
8
+ TestingRunParams,
9
+ setTokenAmount,
10
+ setUSDCAmount,
11
+ testingHelper,
12
+ wait
13
+ } from "./utils/testingHelper";
14
+
15
+ import { getTxOptions } from "./txOptions";
16
+ import BigNumber from "bignumber.js";
17
+ import { balanceDelta } from "./utils/token";
18
+
19
+ const testPendle = ({ wallet, network, provider }: TestingRunParams) => {
20
+ const USDC = CONTRACT_ADDRESS[network].USDC;
21
+ const weETH = "0x35751007a407ca6feffe80b3cb397736d2cf4dbe";
22
+ const PTweETH = "0xb33808ea0e883138680ba29311a220a7377cdb92";
23
+ const PTweETH_matured = "0xe2b2d203577c7cb3d043e89ccf90b5e24d19b66f";
24
+
25
+ let dhedge: Dhedge;
26
+ let pool: Pool;
27
+ jest.setTimeout(100000);
28
+
29
+ describe(`pool on ${network}`, () => {
30
+ beforeAll(async () => {
31
+ dhedge = new Dhedge(wallet, network);
32
+ pool = await dhedge.loadPool(wallet.address, false);
33
+ // top up gas
34
+ await provider.send("hardhat_setBalance", [
35
+ wallet.address,
36
+ "0x10000000000000000"
37
+ ]);
38
+ await provider.send("evm_mine", []);
39
+ // top up USDC
40
+ await setUSDCAmount({
41
+ amount: new BigNumber(2000).times(1e6).toFixed(0),
42
+ userAddress: pool.address,
43
+ network,
44
+ provider
45
+ });
46
+ await pool.approve(Dapp.ODOS, USDC, MAX_AMOUNT);
47
+ await pool.trade(
48
+ Dapp.ODOS,
49
+ USDC,
50
+ weETH,
51
+ "2000000000",
52
+ 0.5,
53
+ await getTxOptions(network)
54
+ );
55
+ });
56
+
57
+ it("swaps weETH to PTweETH on Pendle", async () => {
58
+ await pool.approve(Dapp.PENDLE, weETH, MAX_AMOUNT);
59
+ const weEthBalance = await pool.utils.getBalance(weETH, pool.address);
60
+ await pool.trade(
61
+ Dapp.PENDLE,
62
+ weETH,
63
+ PTweETH,
64
+ weEthBalance,
65
+ 0.5,
66
+ await getTxOptions(network)
67
+ );
68
+ const ptWeEthBalanceDelta = await balanceDelta(
69
+ pool.address,
70
+ PTweETH,
71
+ pool.signer
72
+ );
73
+ expect(ptWeEthBalanceDelta.gt(0));
74
+ });
75
+
76
+ it("swaps PTweETH to weETH on Pendle", async () => {
77
+ await pool.approve(Dapp.PENDLE, PTweETH, MAX_AMOUNT);
78
+ const PTweEthBalance = await pool.utils.getBalance(PTweETH, pool.address);
79
+ console.log("PTweEthBalance", PTweEthBalance.toString());
80
+ await wait(3);
81
+ await pool.trade(
82
+ Dapp.PENDLE,
83
+ PTweETH,
84
+ weETH,
85
+ PTweEthBalance,
86
+ 0.5,
87
+ await getTxOptions(network)
88
+ );
89
+ const weEthBalanceDelta = await balanceDelta(
90
+ pool.address,
91
+ weETH,
92
+ pool.signer
93
+ );
94
+ expect(weEthBalanceDelta.gt(0));
95
+ });
96
+
97
+ it("exit matured PTweETH to weETH on Pendle", async () => {
98
+ await setTokenAmount({
99
+ amount: new BigNumber(1).times(1e18).toString(),
100
+ provider,
101
+ tokenAddress: PTweETH_matured,
102
+ slot: 0,
103
+ userAddress: pool.address
104
+ });
105
+ await pool.approve(Dapp.PENDLE, PTweETH_matured, MAX_AMOUNT);
106
+ const PTweEthBalance = await pool.utils.getBalance(
107
+ PTweETH_matured,
108
+ pool.address
109
+ );
110
+ await wait(3);
111
+ await pool.trade(
112
+ Dapp.PENDLE,
113
+ PTweETH_matured,
114
+ weETH,
115
+ PTweEthBalance,
116
+ 0.5,
117
+ await getTxOptions(network)
118
+ );
119
+ const weEthBalanceDelta = await balanceDelta(
120
+ pool.address,
121
+ weETH,
122
+ pool.signer
123
+ );
124
+ expect(weEthBalanceDelta.eq(PTweEthBalance));
125
+ });
126
+ });
127
+ };
128
+
129
+ testingHelper({
130
+ network: Network.ARBITRUM,
131
+ testingRun: testPendle
132
+ });
package/src/types.ts CHANGED
@@ -31,7 +31,8 @@ export enum Dapp {
31
31
  PANCAKECL = "pancakeCL",
32
32
  COMPOUNDV3 = "compoundV3",
33
33
  ODOS = "odos",
34
- SHADOWCL = "shadowCL"
34
+ SHADOWCL = "shadowCL",
35
+ PENDLE = "pendle"
35
36
  }
36
37
 
37
38
  export enum Transaction {