@dhedge/v2-sdk 2.2.1 → 2.2.2
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/dist/config.d.ts +3 -0
- package/dist/entities/pool.d.ts +3 -1
- package/dist/services/dytm/index.d.ts +7 -0
- package/dist/services/toros/completeWithdrawal.d.ts +2 -2
- package/dist/services/toros/easySwapper.d.ts +4 -1
- package/dist/services/toros/initWithdrawal.d.ts +4 -1
- package/dist/types.d.ts +1 -0
- package/dist/v2-sdk.cjs.development.js +474 -123
- package/dist/v2-sdk.cjs.development.js.map +1 -1
- package/dist/v2-sdk.cjs.production.min.js +1 -1
- package/dist/v2-sdk.cjs.production.min.js.map +1 -1
- package/dist/v2-sdk.esm.js +474 -123
- package/dist/v2-sdk.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/abi/dytm/IOffice.json +191 -0
- package/src/config.ts +20 -2
- package/src/entities/pool.ts +66 -32
- package/src/services/dytm/index.ts +77 -0
- package/src/services/toros/completeWithdrawal.ts +23 -7
- package/src/services/toros/easySwapper.ts +9 -7
- package/src/services/toros/initWithdrawal.ts +10 -6
- package/src/test/dytm.test.ts +241 -0
- package/src/types.ts +1 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
|
|
3
|
+
import BigNumber from "bignumber.js";
|
|
4
|
+
import { BigNumber as EthersBigNumber, Contract, providers } from "ethers";
|
|
5
|
+
import { Dhedge, Pool } from "..";
|
|
6
|
+
import { Dapp, Network } from "../types";
|
|
7
|
+
import { CONTRACT_ADDRESS, MAX_AMOUNT } from "./constants";
|
|
8
|
+
import {
|
|
9
|
+
TestingRunParams,
|
|
10
|
+
runWithImpersonateAccount,
|
|
11
|
+
setTokenAmount,
|
|
12
|
+
setUSDCAmount,
|
|
13
|
+
testingHelper
|
|
14
|
+
} from "./utils/testingHelper";
|
|
15
|
+
import { balanceDelta } from "./utils/token";
|
|
16
|
+
import { routerAddress } from "../config";
|
|
17
|
+
|
|
18
|
+
const officeAbi = [
|
|
19
|
+
"function getMarketConfig(uint88 market) view returns (address marketConfig)"
|
|
20
|
+
];
|
|
21
|
+
const marketConfigAbi = ["function hooks() view returns (address hooks)"];
|
|
22
|
+
const borrowerWhitelistAbi = [
|
|
23
|
+
"function owner() view returns (address)",
|
|
24
|
+
"function setAddressWhitelist(address accountOwner, bool allowed)"
|
|
25
|
+
];
|
|
26
|
+
const erc20AllowanceAbi = [
|
|
27
|
+
"function allowance(address owner, address spender) view returns (uint256)"
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const DYTM_MARKET_ID = 1;
|
|
31
|
+
// ReserveKey: (marketId uint88 << 160) | asset address — DYTM src/types/ReserveKey.sol
|
|
32
|
+
const toReserveKey = (marketId: number, asset: string): EthersBigNumber =>
|
|
33
|
+
EthersBigNumber.from(marketId)
|
|
34
|
+
.shl(160)
|
|
35
|
+
.or(asset);
|
|
36
|
+
// full tokenId: (TokenType << 248) | reserveKey — DYTM src/libraries/TokenHelpers.sol
|
|
37
|
+
// TokenType: ESCROW = 1, LEND = 2, DEBT = 3
|
|
38
|
+
const toLentId = (key: EthersBigNumber): EthersBigNumber =>
|
|
39
|
+
EthersBigNumber.from(2)
|
|
40
|
+
.shl(248)
|
|
41
|
+
.or(key);
|
|
42
|
+
const toEscrowId = (key: EthersBigNumber): EthersBigNumber =>
|
|
43
|
+
EthersBigNumber.from(1)
|
|
44
|
+
.shl(248)
|
|
45
|
+
.or(key);
|
|
46
|
+
|
|
47
|
+
// SPYon (Ondo) collateral on mainnet market 1
|
|
48
|
+
const SPYON_MAINNET = "0xFeDC5f4a6c38211c1338aa411018DFAf26612c08";
|
|
49
|
+
const SPYON_BALANCEOF_SLOT = 51; // OZ ERC20Upgradeable layout
|
|
50
|
+
|
|
51
|
+
// market 1 borrows are gated by the BorrowerWhitelist hook — impersonate its
|
|
52
|
+
// owner and whitelist the pool
|
|
53
|
+
const whitelistPoolAsBorrower = async ({
|
|
54
|
+
pool,
|
|
55
|
+
network,
|
|
56
|
+
provider
|
|
57
|
+
}: {
|
|
58
|
+
pool: Pool;
|
|
59
|
+
network: Network;
|
|
60
|
+
provider: providers.JsonRpcProvider;
|
|
61
|
+
}): Promise<void> => {
|
|
62
|
+
const office = new Contract(
|
|
63
|
+
routerAddress[network][Dapp.DYTM]!,
|
|
64
|
+
officeAbi,
|
|
65
|
+
provider
|
|
66
|
+
);
|
|
67
|
+
const marketConfigAddress = await office.getMarketConfig(DYTM_MARKET_ID);
|
|
68
|
+
const hookAddress = await new Contract(
|
|
69
|
+
marketConfigAddress,
|
|
70
|
+
marketConfigAbi,
|
|
71
|
+
provider
|
|
72
|
+
).hooks();
|
|
73
|
+
const hook = new Contract(hookAddress, borrowerWhitelistAbi, provider);
|
|
74
|
+
const hookOwner = await hook.owner();
|
|
75
|
+
await runWithImpersonateAccount(
|
|
76
|
+
{ account: hookOwner, provider },
|
|
77
|
+
async ({ signer }) => {
|
|
78
|
+
await hook.connect(signer).setAddressWhitelist(pool.address, true);
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const testDytm = ({ network, wallet, provider }: TestingRunParams) => {
|
|
84
|
+
const USDC = CONTRACT_ADDRESS[network].USDC;
|
|
85
|
+
const usdcKey = toReserveKey(DYTM_MARKET_ID, USDC);
|
|
86
|
+
// supply/withdraw take the full LEND tokenId; borrow/repay take the bare ReserveKey
|
|
87
|
+
const USDC_DEPOSIT_TOKEN = toLentId(usdcKey).toString();
|
|
88
|
+
const USDC_BORROW_TOKEN = usdcKey.toString();
|
|
89
|
+
|
|
90
|
+
const USDC_FUNDING_AMOUNT = new BigNumber(100).shiftedBy(6).toFixed(0);
|
|
91
|
+
const USDC_LIQUIDITY_AMOUNT = new BigNumber(50).shiftedBy(6).toFixed(0);
|
|
92
|
+
|
|
93
|
+
// the asset backing the borrow differs per network: on arbitrum lent USDC
|
|
94
|
+
// counts as collateral, on mainnet USDC has lend weight 0 and SPYon escrow
|
|
95
|
+
// collateral (weight 0.75) is required instead
|
|
96
|
+
const collateral =
|
|
97
|
+
network === Network.ETHEREUM
|
|
98
|
+
? {
|
|
99
|
+
asset: SPYON_MAINNET,
|
|
100
|
+
tokenId: toEscrowId(
|
|
101
|
+
toReserveKey(DYTM_MARKET_ID, SPYON_MAINNET)
|
|
102
|
+
).toString(),
|
|
103
|
+
supplyAmount: new BigNumber(1).shiftedBy(18).toFixed(0),
|
|
104
|
+
// SPYon isn't covered by the USDC funding — deal it via its balanceOf slot
|
|
105
|
+
funding: {
|
|
106
|
+
slot: SPYON_BALANCEOF_SLOT,
|
|
107
|
+
amount: new BigNumber(10).shiftedBy(18).toFixed(0)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
: {
|
|
111
|
+
asset: USDC,
|
|
112
|
+
tokenId: USDC_DEPOSIT_TOKEN,
|
|
113
|
+
supplyAmount: USDC_LIQUIDITY_AMOUNT,
|
|
114
|
+
funding: null // covered by the USDC funded in beforeAll
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let dhedge: Dhedge;
|
|
118
|
+
let pool: Pool;
|
|
119
|
+
jest.setTimeout(100000);
|
|
120
|
+
|
|
121
|
+
describe(`[${network}] DYTM tests`, () => {
|
|
122
|
+
beforeAll(async () => {
|
|
123
|
+
// top up ETH (gas)
|
|
124
|
+
await provider.send("hardhat_setBalance", [
|
|
125
|
+
wallet.address,
|
|
126
|
+
"0x100000000000000"
|
|
127
|
+
]);
|
|
128
|
+
// mine a local block so eth_calls run under hardhat's own hardfork —
|
|
129
|
+
// the remote fork-point block executes as london (hardforkHistory) and
|
|
130
|
+
// lacks the cancun opcodes DYTM needs (transient storage)
|
|
131
|
+
await provider.send("evm_mine", []);
|
|
132
|
+
dhedge = new Dhedge(wallet, network);
|
|
133
|
+
pool = await dhedge.loadPool(wallet.address, false);
|
|
134
|
+
|
|
135
|
+
await whitelistPoolAsBorrower({ pool, network, provider });
|
|
136
|
+
|
|
137
|
+
// fund USDC and supply half of it as market liquidity to be borrowed
|
|
138
|
+
await setUSDCAmount({
|
|
139
|
+
amount: USDC_FUNDING_AMOUNT,
|
|
140
|
+
userAddress: pool.address,
|
|
141
|
+
network,
|
|
142
|
+
provider
|
|
143
|
+
});
|
|
144
|
+
await pool.approve(Dapp.DYTM, USDC, MAX_AMOUNT);
|
|
145
|
+
await pool.lend(Dapp.DYTM, USDC_DEPOSIT_TOKEN, USDC_LIQUIDITY_AMOUNT);
|
|
146
|
+
|
|
147
|
+
// fund the collateral asset for the supply tests
|
|
148
|
+
if (collateral.funding) {
|
|
149
|
+
await setTokenAmount({
|
|
150
|
+
amount: collateral.funding.amount,
|
|
151
|
+
userAddress: pool.address,
|
|
152
|
+
tokenAddress: collateral.asset,
|
|
153
|
+
slot: collateral.funding.slot,
|
|
154
|
+
provider
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("approves unlimited collateral for DYTM", async () => {
|
|
160
|
+
await pool.approve(Dapp.DYTM, collateral.asset, MAX_AMOUNT);
|
|
161
|
+
const allowance = await new Contract(
|
|
162
|
+
collateral.asset,
|
|
163
|
+
erc20AllowanceAbi,
|
|
164
|
+
provider
|
|
165
|
+
).allowance(pool.address, routerAddress[network][Dapp.DYTM]!);
|
|
166
|
+
expect(allowance.eq(MAX_AMOUNT)).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("estimates supplying collateral to DYTM market", async () => {
|
|
170
|
+
const result = await pool.lend(
|
|
171
|
+
Dapp.DYTM,
|
|
172
|
+
collateral.tokenId,
|
|
173
|
+
collateral.supplyAmount,
|
|
174
|
+
0,
|
|
175
|
+
null,
|
|
176
|
+
true
|
|
177
|
+
);
|
|
178
|
+
expect(result.gas.gt(0)).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("supplies collateral to DYTM market", async () => {
|
|
182
|
+
await pool.lend(Dapp.DYTM, collateral.tokenId, collateral.supplyAmount);
|
|
183
|
+
|
|
184
|
+
const collateralDelta = await balanceDelta(
|
|
185
|
+
pool.address,
|
|
186
|
+
collateral.asset,
|
|
187
|
+
pool.signer
|
|
188
|
+
);
|
|
189
|
+
expect(collateralDelta.lt(0)).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("borrows USDC from DYTM market", async () => {
|
|
193
|
+
// borrow value must clear the market's minDebtAmountUSD ($1 on arbitrum);
|
|
194
|
+
// borrow 3 USDC so the ~2 USDC debt left after the repay test stays above it
|
|
195
|
+
await pool.borrow(Dapp.DYTM, USDC_BORROW_TOKEN, (3 * 1e6).toString());
|
|
196
|
+
|
|
197
|
+
const usdcTokenDelta = await balanceDelta(
|
|
198
|
+
pool.address,
|
|
199
|
+
USDC,
|
|
200
|
+
pool.signer
|
|
201
|
+
);
|
|
202
|
+
expect(usdcTokenDelta.gt(0)).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("repays USDC to DYTM market", async () => {
|
|
206
|
+
await pool.repay(Dapp.DYTM, USDC_BORROW_TOKEN, (1 * 1e6).toString());
|
|
207
|
+
|
|
208
|
+
const usdcTokenDelta = await balanceDelta(
|
|
209
|
+
pool.address,
|
|
210
|
+
USDC,
|
|
211
|
+
pool.signer
|
|
212
|
+
);
|
|
213
|
+
expect(usdcTokenDelta.lt(0)).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("withdraws USDC from DYTM market", async () => {
|
|
217
|
+
await pool.withdrawDeposit(
|
|
218
|
+
Dapp.DYTM,
|
|
219
|
+
USDC_DEPOSIT_TOKEN,
|
|
220
|
+
(5 * 1e6).toString()
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const usdcTokenDelta = await balanceDelta(
|
|
224
|
+
pool.address,
|
|
225
|
+
USDC,
|
|
226
|
+
pool.signer
|
|
227
|
+
);
|
|
228
|
+
expect(usdcTokenDelta.gt(0)).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
testingHelper({
|
|
234
|
+
network: Network.ARBITRUM,
|
|
235
|
+
testingRun: testDytm
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
testingHelper({
|
|
239
|
+
network: Network.ETHEREUM,
|
|
240
|
+
testingRun: testDytm
|
|
241
|
+
});
|