deploy-sdk 0.0.1
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/README.md +317 -0
- package/dist/base-01mNzZ7A.d.mts +56 -0
- package/dist/base-01mNzZ7A.d.ts +56 -0
- package/dist/base-DLLHDCZ5.d.mts +56 -0
- package/dist/base-DLLHDCZ5.d.ts +56 -0
- package/dist/chunk-ELR7YJ6L.mjs +10 -0
- package/dist/chunk-F2AHC5HC.mjs +94 -0
- package/dist/chunk-QZGD3DFS.mjs +1 -0
- package/dist/chunk-QZYDPY65.mjs +733 -0
- package/dist/chunk-X3SXEVUG.mjs +732 -0
- package/dist/chunk-XSAEU2QV.mjs +1 -0
- package/dist/chunk-YEI5UNBO.mjs +1 -0
- package/dist/index-B9tOEGVF.d.mts +192 -0
- package/dist/index-CY6ZN0n3.d.ts +192 -0
- package/dist/index-DITBfiHE.d.mts +192 -0
- package/dist/index-DhDHmxb0.d.ts +192 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/modules/index.d.mts +4 -0
- package/dist/modules/index.d.ts +4 -0
- package/dist/modules/index.js +1 -0
- package/dist/modules/index.mjs +1 -0
- package/dist/wallet-adapters/index.d.mts +38 -0
- package/dist/wallet-adapters/index.d.ts +38 -0
- package/dist/wallet-adapters/index.js +1 -0
- package/dist/wallet-adapters/index.mjs +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
// src/modules/mint/MintModule.ts
|
|
2
|
+
import { Contract } from "ethers";
|
|
3
|
+
|
|
4
|
+
// src/core/errors.ts
|
|
5
|
+
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
6
|
+
ErrorCode2["NOT_INITIALIZED"] = "NOT_INITIALIZED";
|
|
7
|
+
ErrorCode2["WALLET_NOT_CONNECTED"] = "WALLET_NOT_CONNECTED";
|
|
8
|
+
ErrorCode2["INVALID_CHAIN"] = "INVALID_CHAIN";
|
|
9
|
+
ErrorCode2["INSUFFICIENT_BALANCE"] = "INSUFFICIENT_BALANCE";
|
|
10
|
+
ErrorCode2["INSUFFICIENT_ALLOWANCE"] = "INSUFFICIENT_ALLOWANCE";
|
|
11
|
+
ErrorCode2["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
|
|
12
|
+
ErrorCode2["STAKE_FAILED"] = "STAKE_FAILED";
|
|
13
|
+
ErrorCode2["UNSTAKE_FAILED"] = "UNSTAKE_FAILED";
|
|
14
|
+
ErrorCode2["COOLDOWN_FAILED"] = "COOLDOWN_FAILED";
|
|
15
|
+
ErrorCode2["MINT_FAILED"] = "MINT_FAILED";
|
|
16
|
+
ErrorCode2["REDEEM_FAILED"] = "REDEEM_FAILED";
|
|
17
|
+
ErrorCode2["INVALID_ORDER"] = "INVALID_ORDER";
|
|
18
|
+
ErrorCode2["SIGNATURE_FAILED"] = "SIGNATURE_FAILED";
|
|
19
|
+
ErrorCode2["API_ERROR"] = "API_ERROR";
|
|
20
|
+
ErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
21
|
+
return ErrorCode2;
|
|
22
|
+
})(ErrorCode || {});
|
|
23
|
+
var DeploySDKError = class _DeploySDKError extends Error {
|
|
24
|
+
constructor(code, message, originalError) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "DeploySDKError";
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.originalError = originalError;
|
|
29
|
+
if (Error.captureStackTrace) {
|
|
30
|
+
Error.captureStackTrace(this, _DeploySDKError);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
toJSON() {
|
|
34
|
+
return {
|
|
35
|
+
name: this.name,
|
|
36
|
+
code: this.code,
|
|
37
|
+
message: this.message,
|
|
38
|
+
originalError: this.originalError?.message || this.originalError
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/core/constants.ts
|
|
44
|
+
var CONTRACT_ADDRESSES = {
|
|
45
|
+
[1 /* ETHEREUM */]: {
|
|
46
|
+
USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
|
47
|
+
USDT: "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
|
48
|
+
dUSD: "0xf42e0b98e32150fe02a370456e6479fcd94f5531",
|
|
49
|
+
DUSD_MINTER: "0x1ee453Ea35f6EAbD9BFF126586322cbC906D4EB3",
|
|
50
|
+
sDUSD_STAKING: "0x7f37B0133B1adC1D0647EE52eA38fA13caC4aA1B"
|
|
51
|
+
},
|
|
52
|
+
[42161 /* ARBITRUM */]: {}
|
|
53
|
+
};
|
|
54
|
+
var COLLATERAL_ASSETS = {
|
|
55
|
+
USDC: {
|
|
56
|
+
key: "USDC",
|
|
57
|
+
name: "USD Coin",
|
|
58
|
+
symbol: "USDC",
|
|
59
|
+
address: CONTRACT_ADDRESSES[1 /* ETHEREUM */].USDC,
|
|
60
|
+
decimals: 6,
|
|
61
|
+
mintingContract: CONTRACT_ADDRESSES[1 /* ETHEREUM */].DUSD_MINTER
|
|
62
|
+
},
|
|
63
|
+
USDT: {
|
|
64
|
+
key: "USDT",
|
|
65
|
+
name: "Tether USD",
|
|
66
|
+
symbol: "USDT",
|
|
67
|
+
address: CONTRACT_ADDRESSES[1 /* ETHEREUM */].USDT,
|
|
68
|
+
decimals: 6,
|
|
69
|
+
mintingContract: CONTRACT_ADDRESSES[1 /* ETHEREUM */].DUSD_MINTER
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var STAKE_TOKENS = {
|
|
73
|
+
dUSD: {
|
|
74
|
+
key: "dUSD",
|
|
75
|
+
name: "Deploy USD",
|
|
76
|
+
symbol: "dUSD",
|
|
77
|
+
address: CONTRACT_ADDRESSES[1 /* ETHEREUM */].dUSD,
|
|
78
|
+
decimals: 18,
|
|
79
|
+
supportedCollateral: [COLLATERAL_ASSETS.USDC, COLLATERAL_ASSETS.USDT],
|
|
80
|
+
mintingContract: CONTRACT_ADDRESSES[1 /* ETHEREUM */].DUSD_MINTER,
|
|
81
|
+
stakingContract: CONTRACT_ADDRESSES[1 /* ETHEREUM */].sDUSD_STAKING,
|
|
82
|
+
stakingSymbol: "sDUSD",
|
|
83
|
+
cooldownPeriod: 90 * 24 * 60 * 60 * 1e3
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var EIP712_TYPES_ORDER = {
|
|
87
|
+
Order: [
|
|
88
|
+
{ name: "order_id", type: "string" },
|
|
89
|
+
{ name: "order_type", type: "uint8" },
|
|
90
|
+
{ name: "expiry", type: "uint256" },
|
|
91
|
+
{ name: "nonce", type: "uint256" },
|
|
92
|
+
{ name: "benefactor", type: "address" },
|
|
93
|
+
{ name: "beneficiary", type: "address" },
|
|
94
|
+
{ name: "collateral_asset", type: "address" },
|
|
95
|
+
{ name: "collateral_amount", type: "uint256" },
|
|
96
|
+
{ name: "dAsset_amount", type: "uint256" }
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
var ERC4626_ABI = [
|
|
100
|
+
"function deposit(uint256 assets, address receiver) external returns (uint256 shares)",
|
|
101
|
+
"function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares)",
|
|
102
|
+
"function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets)",
|
|
103
|
+
"function cooldownShares(uint256 shares) external",
|
|
104
|
+
"function cooldowns(address user) external view returns (uint256 cooldownEnd, uint256 underlyingAmount)",
|
|
105
|
+
"function balanceOf(address owner) external view returns (uint256)",
|
|
106
|
+
"function convertToShares(uint256 assets) external view returns (uint256)",
|
|
107
|
+
"function convertToAssets(uint256 shares) external view returns (uint256)",
|
|
108
|
+
"function totalAssets() external view returns (uint256)",
|
|
109
|
+
"function totalSupply() external view returns (uint256)",
|
|
110
|
+
"event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)",
|
|
111
|
+
"event Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)"
|
|
112
|
+
];
|
|
113
|
+
var ERC20_ABI = [
|
|
114
|
+
"function approve(address spender, uint256 amount) external returns (bool)",
|
|
115
|
+
"function allowance(address owner, address spender) external view returns (uint256)",
|
|
116
|
+
"function balanceOf(address account) external view returns (uint256)",
|
|
117
|
+
"function decimals() external view returns (uint8)",
|
|
118
|
+
"function symbol() external view returns (string)",
|
|
119
|
+
"function name() external view returns (string)",
|
|
120
|
+
"function transfer(address recipient, uint256 amount) external returns (bool)",
|
|
121
|
+
"function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)",
|
|
122
|
+
"event Approval(address indexed owner, address indexed spender, uint256 value)",
|
|
123
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)"
|
|
124
|
+
];
|
|
125
|
+
var MINTER_ABI = [
|
|
126
|
+
"function mint(tuple(string order_id, uint8 order_type, uint256 expiry, uint256 nonce, address benefactor, address beneficiary, address collateral_asset, uint256 collateral_amount, uint256 dAsset_amount) order, tuple(address[] addresses, uint256[] ratios) route, tuple(uint8 signature_type, bytes signature_bytes) signature) external",
|
|
127
|
+
"function redeem(tuple(string order_id, uint8 order_type, uint256 expiry, uint256 nonce, address benefactor, address beneficiary, address collateral_asset, uint256 collateral_amount, uint256 dAsset_amount) order, tuple(uint8 signature_type, bytes signature_bytes) signature) external",
|
|
128
|
+
"function isWhitelistedBenefactor(address benefactor) external view returns (bool)",
|
|
129
|
+
"function addWhitelistedBenefactor(address benefactor) external",
|
|
130
|
+
"function removeWhitelistedBenefactor(address benefactor) external"
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
// src/modules/mint/MintModule.ts
|
|
134
|
+
import { v4 as uuidv4 } from "uuid";
|
|
135
|
+
var MintModule = class {
|
|
136
|
+
constructor(sdk) {
|
|
137
|
+
this.sdk = sdk;
|
|
138
|
+
}
|
|
139
|
+
async createOrder(collateralAsset, collateralAmount, dAssetAmount, options) {
|
|
140
|
+
const address = await this.sdk.getAddress();
|
|
141
|
+
const orderId = uuidv4();
|
|
142
|
+
const expiry = Math.floor(Date.now() / 1e3) + (options?.expiryMinutes || 5) * 60;
|
|
143
|
+
const minterAddress = collateralAsset.mintingContract;
|
|
144
|
+
const order = {
|
|
145
|
+
orderId,
|
|
146
|
+
orderType: 0,
|
|
147
|
+
expiry,
|
|
148
|
+
nonce: Date.now(),
|
|
149
|
+
benefactor: options?.benefactor || address,
|
|
150
|
+
beneficiary: options?.beneficiary || address,
|
|
151
|
+
collateralAsset: collateralAsset.address,
|
|
152
|
+
collateralAmount: collateralAmount.toString(),
|
|
153
|
+
dAssetAmount: dAssetAmount.toString(),
|
|
154
|
+
minterAddress
|
|
155
|
+
};
|
|
156
|
+
return order;
|
|
157
|
+
}
|
|
158
|
+
async signOrder(order) {
|
|
159
|
+
const signer = this.sdk.signer;
|
|
160
|
+
const domain = {
|
|
161
|
+
name: "DeployMinting",
|
|
162
|
+
version: "1",
|
|
163
|
+
chainId: await this.sdk.getChainId(),
|
|
164
|
+
verifyingContract: order.minterAddress
|
|
165
|
+
};
|
|
166
|
+
const value = {
|
|
167
|
+
order_id: order.orderId,
|
|
168
|
+
order_type: order.orderType,
|
|
169
|
+
expiry: order.expiry,
|
|
170
|
+
nonce: order.nonce,
|
|
171
|
+
benefactor: order.benefactor,
|
|
172
|
+
beneficiary: order.beneficiary,
|
|
173
|
+
collateral_asset: order.collateralAsset,
|
|
174
|
+
collateral_amount: order.collateralAmount,
|
|
175
|
+
dAsset_amount: order.dAssetAmount
|
|
176
|
+
};
|
|
177
|
+
try {
|
|
178
|
+
const signature = await signer._signTypedData(domain, EIP712_TYPES_ORDER, value);
|
|
179
|
+
return signature;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw new DeploySDKError(
|
|
182
|
+
"SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
|
|
183
|
+
"Failed to sign mint order",
|
|
184
|
+
error
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async submitOrder(order, signature) {
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch(`${this.sdk.config.apiUrl}/api/mint`, {
|
|
191
|
+
method: "POST",
|
|
192
|
+
headers: {
|
|
193
|
+
"Content-Type": "application/json"
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
order: {
|
|
197
|
+
order_id: order.orderId,
|
|
198
|
+
order_type: order.orderType,
|
|
199
|
+
expiry: order.expiry,
|
|
200
|
+
nonce: order.nonce,
|
|
201
|
+
benefactor: order.benefactor,
|
|
202
|
+
beneficiary: order.beneficiary,
|
|
203
|
+
collateral_asset: order.collateralAsset,
|
|
204
|
+
collateral_amount: order.collateralAmount,
|
|
205
|
+
dAsset_amount: order.dAssetAmount
|
|
206
|
+
},
|
|
207
|
+
signature: {
|
|
208
|
+
signature_type: 0,
|
|
209
|
+
signature_bytes: signature
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
const error = await response.json();
|
|
215
|
+
throw new DeploySDKError(
|
|
216
|
+
"API_ERROR" /* API_ERROR */,
|
|
217
|
+
error.message || "Failed to submit mint order"
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const result = await response.json();
|
|
221
|
+
return {
|
|
222
|
+
success: true,
|
|
223
|
+
orderId: order.orderId,
|
|
224
|
+
txHash: result.txHash,
|
|
225
|
+
status: result.status
|
|
226
|
+
};
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (error instanceof DeploySDKError) throw error;
|
|
229
|
+
throw new DeploySDKError(
|
|
230
|
+
"API_ERROR" /* API_ERROR */,
|
|
231
|
+
"Failed to submit mint order",
|
|
232
|
+
error
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async mint(collateralAsset, collateralAmount, dAssetAmount, options) {
|
|
237
|
+
try {
|
|
238
|
+
const order = await this.createOrder(
|
|
239
|
+
collateralAsset,
|
|
240
|
+
collateralAmount,
|
|
241
|
+
dAssetAmount,
|
|
242
|
+
options
|
|
243
|
+
);
|
|
244
|
+
const signature = await this.signOrder(order);
|
|
245
|
+
return await this.submitOrder(order, signature);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof DeploySDKError) throw error;
|
|
248
|
+
throw new DeploySDKError(
|
|
249
|
+
"MINT_FAILED" /* MINT_FAILED */,
|
|
250
|
+
"Failed to mint tokens",
|
|
251
|
+
error
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async estimateGas(collateralAsset, collateralAmount, dAssetAmount, signature) {
|
|
256
|
+
const minterContract = new Contract(
|
|
257
|
+
collateralAsset.mintingContract,
|
|
258
|
+
MINTER_ABI,
|
|
259
|
+
this.sdk.provider
|
|
260
|
+
);
|
|
261
|
+
const order = {
|
|
262
|
+
order_id: uuidv4(),
|
|
263
|
+
order_type: 0,
|
|
264
|
+
expiry: Math.floor(Date.now() / 1e3) + 300,
|
|
265
|
+
nonce: Date.now(),
|
|
266
|
+
benefactor: await this.sdk.getAddress(),
|
|
267
|
+
beneficiary: await this.sdk.getAddress(),
|
|
268
|
+
collateral_asset: collateralAsset.address,
|
|
269
|
+
collateral_amount: collateralAmount.toString(),
|
|
270
|
+
dAsset_amount: dAssetAmount.toString()
|
|
271
|
+
};
|
|
272
|
+
try {
|
|
273
|
+
const gasEstimate = await minterContract.estimateGas.mint(
|
|
274
|
+
order,
|
|
275
|
+
{ addresses: [], ratios: [] },
|
|
276
|
+
{ signature_type: 0, signature_bytes: signature }
|
|
277
|
+
);
|
|
278
|
+
return gasEstimate.toBigInt();
|
|
279
|
+
} catch (error) {
|
|
280
|
+
throw new DeploySDKError(
|
|
281
|
+
"TRANSACTION_FAILED" /* TRANSACTION_FAILED */,
|
|
282
|
+
"Failed to estimate gas for mint",
|
|
283
|
+
error
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// src/modules/stake/StakeModule.ts
|
|
290
|
+
import { Contract as Contract2, BigNumber } from "ethers";
|
|
291
|
+
var StakeModule = class {
|
|
292
|
+
constructor(sdk) {
|
|
293
|
+
this.sdk = sdk;
|
|
294
|
+
}
|
|
295
|
+
async stake(params) {
|
|
296
|
+
try {
|
|
297
|
+
const { token, amount, receiver } = params;
|
|
298
|
+
if (!token.stakingContract) {
|
|
299
|
+
throw new DeploySDKError(
|
|
300
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
301
|
+
`Token ${token.symbol} does not support staking`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
const vaultContract = new Contract2(
|
|
305
|
+
token.stakingContract,
|
|
306
|
+
ERC4626_ABI,
|
|
307
|
+
this.sdk.signer
|
|
308
|
+
);
|
|
309
|
+
const receiverAddress = receiver || await this.sdk.getAddress();
|
|
310
|
+
const tx = await vaultContract.deposit(amount, receiverAddress);
|
|
311
|
+
const receipt = await tx.wait();
|
|
312
|
+
let sharesReceived;
|
|
313
|
+
const depositEvent = receipt.events?.find(
|
|
314
|
+
(e) => e.event === "Deposit"
|
|
315
|
+
);
|
|
316
|
+
if (depositEvent) {
|
|
317
|
+
sharesReceived = depositEvent.args?.shares?.toString();
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
success: true,
|
|
321
|
+
txHash: receipt.transactionHash,
|
|
322
|
+
sharesReceived
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error instanceof DeploySDKError) throw error;
|
|
326
|
+
throw new DeploySDKError(
|
|
327
|
+
"STAKE_FAILED" /* STAKE_FAILED */,
|
|
328
|
+
"Failed to stake tokens",
|
|
329
|
+
error
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async getPosition(token, userAddress) {
|
|
334
|
+
if (!token.stakingContract) {
|
|
335
|
+
throw new DeploySDKError(
|
|
336
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
337
|
+
`Token ${token.symbol} does not support staking`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
const address = userAddress || await this.sdk.getAddress();
|
|
341
|
+
const vaultContract = new Contract2(
|
|
342
|
+
token.stakingContract,
|
|
343
|
+
ERC4626_ABI,
|
|
344
|
+
this.sdk.provider
|
|
345
|
+
);
|
|
346
|
+
try {
|
|
347
|
+
const [shares, assets, cooldown] = await Promise.all([
|
|
348
|
+
vaultContract.balanceOf(address),
|
|
349
|
+
vaultContract.convertToAssets(await vaultContract.balanceOf(address)),
|
|
350
|
+
vaultContract.cooldowns(address).catch(() => ({ cooldownEnd: BigNumber.from(0), underlyingAmount: BigNumber.from(0) }))
|
|
351
|
+
]);
|
|
352
|
+
const cooldownEnd = cooldown.cooldownEnd.toNumber() * 1e3;
|
|
353
|
+
return {
|
|
354
|
+
tokenKey: token.key,
|
|
355
|
+
stakedAmount: shares.toString(),
|
|
356
|
+
stakedValue: assets.toString(),
|
|
357
|
+
cooldownEnd: cooldownEnd > 0 ? cooldownEnd : void 0,
|
|
358
|
+
canUnstake: cooldownEnd === 0 || cooldownEnd <= Date.now()
|
|
359
|
+
};
|
|
360
|
+
} catch (error) {
|
|
361
|
+
throw new DeploySDKError(
|
|
362
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
363
|
+
"Failed to fetch staking position",
|
|
364
|
+
error
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async previewStake(token, amount) {
|
|
369
|
+
if (!token.stakingContract) {
|
|
370
|
+
throw new DeploySDKError(
|
|
371
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
372
|
+
`Token ${token.symbol} does not support staking`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
const vaultContract = new Contract2(
|
|
376
|
+
token.stakingContract,
|
|
377
|
+
ERC4626_ABI,
|
|
378
|
+
this.sdk.provider
|
|
379
|
+
);
|
|
380
|
+
try {
|
|
381
|
+
const shares = await vaultContract.convertToShares(amount);
|
|
382
|
+
return {
|
|
383
|
+
shares: shares.toString(),
|
|
384
|
+
assets: amount.toString()
|
|
385
|
+
};
|
|
386
|
+
} catch (error) {
|
|
387
|
+
throw new DeploySDKError(
|
|
388
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
389
|
+
"Failed to preview stake",
|
|
390
|
+
error
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async getAPY(token) {
|
|
395
|
+
if (!token.stakingContract) {
|
|
396
|
+
return 0;
|
|
397
|
+
}
|
|
398
|
+
const vaultContract = new Contract2(
|
|
399
|
+
token.stakingContract,
|
|
400
|
+
ERC4626_ABI,
|
|
401
|
+
this.sdk.provider
|
|
402
|
+
);
|
|
403
|
+
try {
|
|
404
|
+
const [totalAssets, totalSupply] = await Promise.all([
|
|
405
|
+
vaultContract.totalAssets(),
|
|
406
|
+
vaultContract.totalSupply()
|
|
407
|
+
]);
|
|
408
|
+
if (totalSupply.isZero()) {
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
const sharePrice = totalAssets.mul(BigNumber.from(10).pow(18)).div(totalSupply);
|
|
412
|
+
const basePrice = BigNumber.from(10).pow(18);
|
|
413
|
+
if (sharePrice.lte(basePrice)) {
|
|
414
|
+
return 0;
|
|
415
|
+
}
|
|
416
|
+
const yieldPerShare = sharePrice.sub(basePrice);
|
|
417
|
+
const dailyYield = yieldPerShare.mul(365);
|
|
418
|
+
const apy = dailyYield.mul(1e4).div(basePrice).toNumber() / 100;
|
|
419
|
+
return apy;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
return 0;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// src/modules/unstake/UnstakeModule.ts
|
|
427
|
+
import { Contract as Contract3 } from "ethers";
|
|
428
|
+
var UnstakeModule = class {
|
|
429
|
+
constructor(sdk) {
|
|
430
|
+
this.sdk = sdk;
|
|
431
|
+
}
|
|
432
|
+
async initiateCooldown(token, sharesAmount) {
|
|
433
|
+
try {
|
|
434
|
+
if (!token.stakingContract) {
|
|
435
|
+
throw new DeploySDKError(
|
|
436
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
437
|
+
`Token ${token.symbol} does not support staking`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
const vaultContract = new Contract3(
|
|
441
|
+
token.stakingContract,
|
|
442
|
+
ERC4626_ABI,
|
|
443
|
+
this.sdk.signer
|
|
444
|
+
);
|
|
445
|
+
const tx = await vaultContract.cooldownShares(sharesAmount);
|
|
446
|
+
const receipt = await tx.wait();
|
|
447
|
+
const cooldownEnd = Date.now() + (token.cooldownPeriod || 90 * 24 * 60 * 60 * 1e3);
|
|
448
|
+
return {
|
|
449
|
+
success: true,
|
|
450
|
+
txHash: receipt.transactionHash,
|
|
451
|
+
cooldownEnd
|
|
452
|
+
};
|
|
453
|
+
} catch (error) {
|
|
454
|
+
if (error instanceof DeploySDKError) throw error;
|
|
455
|
+
throw new DeploySDKError(
|
|
456
|
+
"COOLDOWN_FAILED" /* COOLDOWN_FAILED */,
|
|
457
|
+
"Failed to initiate cooldown",
|
|
458
|
+
error
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async unstake(params) {
|
|
463
|
+
try {
|
|
464
|
+
const { token, sharesAmount, receiver } = params;
|
|
465
|
+
if (!token.stakingContract) {
|
|
466
|
+
throw new DeploySDKError(
|
|
467
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
468
|
+
`Token ${token.symbol} does not support staking`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
const cooldownStatus = await this.getCooldownStatus(token);
|
|
472
|
+
if (!cooldownStatus.canUnstake) {
|
|
473
|
+
throw new DeploySDKError(
|
|
474
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
475
|
+
`Cannot unstake. Cooldown ends at ${new Date(cooldownStatus.cooldownEnd).toLocaleString()}`
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
const vaultContract = new Contract3(
|
|
479
|
+
token.stakingContract,
|
|
480
|
+
ERC4626_ABI,
|
|
481
|
+
this.sdk.signer
|
|
482
|
+
);
|
|
483
|
+
const receiverAddress = receiver || await this.sdk.getAddress();
|
|
484
|
+
const owner = await this.sdk.getAddress();
|
|
485
|
+
const tx = await vaultContract.redeem(sharesAmount, receiverAddress, owner);
|
|
486
|
+
const receipt = await tx.wait();
|
|
487
|
+
let assetsWithdrawn;
|
|
488
|
+
const withdrawEvent = receipt.events?.find(
|
|
489
|
+
(e) => e.event === "Withdraw"
|
|
490
|
+
);
|
|
491
|
+
if (withdrawEvent) {
|
|
492
|
+
assetsWithdrawn = withdrawEvent.args?.assets?.toString();
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
success: true,
|
|
496
|
+
txHash: receipt.transactionHash,
|
|
497
|
+
assetsWithdrawn
|
|
498
|
+
};
|
|
499
|
+
} catch (error) {
|
|
500
|
+
if (error instanceof DeploySDKError) throw error;
|
|
501
|
+
throw new DeploySDKError(
|
|
502
|
+
"UNSTAKE_FAILED" /* UNSTAKE_FAILED */,
|
|
503
|
+
"Failed to unstake tokens",
|
|
504
|
+
error
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async getCooldownStatus(token, userAddress) {
|
|
509
|
+
if (!token.stakingContract) {
|
|
510
|
+
throw new DeploySDKError(
|
|
511
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
512
|
+
`Token ${token.symbol} does not support staking`
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
const address = userAddress || await this.sdk.getAddress();
|
|
516
|
+
const vaultContract = new Contract3(
|
|
517
|
+
token.stakingContract,
|
|
518
|
+
ERC4626_ABI,
|
|
519
|
+
this.sdk.provider
|
|
520
|
+
);
|
|
521
|
+
try {
|
|
522
|
+
const cooldown = await vaultContract.cooldowns(address);
|
|
523
|
+
const cooldownEnd = cooldown.cooldownEnd.toNumber() * 1e3;
|
|
524
|
+
return {
|
|
525
|
+
cooldownEnd,
|
|
526
|
+
sharesInCooldown: cooldown.underlyingAmount.toString(),
|
|
527
|
+
canUnstake: cooldownEnd > 0 && cooldownEnd <= Date.now()
|
|
528
|
+
};
|
|
529
|
+
} catch (error) {
|
|
530
|
+
throw new DeploySDKError(
|
|
531
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
532
|
+
"Failed to fetch cooldown status",
|
|
533
|
+
error
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async previewUnstake(token, sharesAmount) {
|
|
538
|
+
if (!token.stakingContract) {
|
|
539
|
+
throw new DeploySDKError(
|
|
540
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
541
|
+
`Token ${token.symbol} does not support staking`
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
const vaultContract = new Contract3(
|
|
545
|
+
token.stakingContract,
|
|
546
|
+
ERC4626_ABI,
|
|
547
|
+
this.sdk.provider
|
|
548
|
+
);
|
|
549
|
+
try {
|
|
550
|
+
const assets = await vaultContract.convertToAssets(sharesAmount);
|
|
551
|
+
return assets.toString();
|
|
552
|
+
} catch (error) {
|
|
553
|
+
throw new DeploySDKError(
|
|
554
|
+
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
|
|
555
|
+
"Failed to preview unstake",
|
|
556
|
+
error
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/modules/redeem/RedeemModule.ts
|
|
563
|
+
import { Contract as Contract4 } from "ethers";
|
|
564
|
+
import { v4 as uuidv42 } from "uuid";
|
|
565
|
+
var RedeemModule = class {
|
|
566
|
+
constructor(sdk) {
|
|
567
|
+
this.sdk = sdk;
|
|
568
|
+
}
|
|
569
|
+
async createOrder(token, redeemAmount, collateralAsset, collateralAmount, options) {
|
|
570
|
+
const address = await this.sdk.getAddress();
|
|
571
|
+
const orderId = uuidv42();
|
|
572
|
+
const expiry = Math.floor(Date.now() / 1e3) + (options?.expiryMinutes || 5) * 60;
|
|
573
|
+
const minterAddress = token.mintingContract;
|
|
574
|
+
const order = {
|
|
575
|
+
orderId,
|
|
576
|
+
orderType: 1,
|
|
577
|
+
expiry,
|
|
578
|
+
nonce: Date.now(),
|
|
579
|
+
benefactor: options?.benefactor || address,
|
|
580
|
+
beneficiary: options?.beneficiary || address,
|
|
581
|
+
collateralAsset,
|
|
582
|
+
collateralAmount: collateralAmount.toString(),
|
|
583
|
+
dAssetAmount: redeemAmount.toString(),
|
|
584
|
+
minterAddress
|
|
585
|
+
};
|
|
586
|
+
return order;
|
|
587
|
+
}
|
|
588
|
+
async signOrder(order) {
|
|
589
|
+
const signer = this.sdk.signer;
|
|
590
|
+
const domain = {
|
|
591
|
+
name: "DeployMinting",
|
|
592
|
+
version: "1",
|
|
593
|
+
chainId: await this.sdk.getChainId(),
|
|
594
|
+
verifyingContract: order.minterAddress
|
|
595
|
+
};
|
|
596
|
+
const value = {
|
|
597
|
+
order_id: order.orderId,
|
|
598
|
+
order_type: order.orderType,
|
|
599
|
+
expiry: order.expiry,
|
|
600
|
+
nonce: order.nonce,
|
|
601
|
+
benefactor: order.benefactor,
|
|
602
|
+
beneficiary: order.beneficiary,
|
|
603
|
+
collateral_asset: order.collateralAsset,
|
|
604
|
+
collateral_amount: order.collateralAmount,
|
|
605
|
+
dAsset_amount: order.dAssetAmount
|
|
606
|
+
};
|
|
607
|
+
try {
|
|
608
|
+
const signature = await signer._signTypedData(domain, EIP712_TYPES_ORDER, value);
|
|
609
|
+
return signature;
|
|
610
|
+
} catch (error) {
|
|
611
|
+
throw new DeploySDKError(
|
|
612
|
+
"SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
|
|
613
|
+
"Failed to sign redeem order",
|
|
614
|
+
error
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async submitOrder(order, signature) {
|
|
619
|
+
try {
|
|
620
|
+
const response = await fetch(`${this.sdk.config.apiUrl}/api/redeem`, {
|
|
621
|
+
method: "POST",
|
|
622
|
+
headers: {
|
|
623
|
+
"Content-Type": "application/json"
|
|
624
|
+
},
|
|
625
|
+
body: JSON.stringify({
|
|
626
|
+
order: {
|
|
627
|
+
order_id: order.orderId,
|
|
628
|
+
order_type: order.orderType,
|
|
629
|
+
expiry: order.expiry,
|
|
630
|
+
nonce: order.nonce,
|
|
631
|
+
benefactor: order.benefactor,
|
|
632
|
+
beneficiary: order.beneficiary,
|
|
633
|
+
collateral_asset: order.collateralAsset,
|
|
634
|
+
collateral_amount: order.collateralAmount,
|
|
635
|
+
dAsset_amount: order.dAssetAmount
|
|
636
|
+
},
|
|
637
|
+
signature: {
|
|
638
|
+
signature_type: 0,
|
|
639
|
+
signature_bytes: signature
|
|
640
|
+
}
|
|
641
|
+
})
|
|
642
|
+
});
|
|
643
|
+
if (!response.ok) {
|
|
644
|
+
const error = await response.json();
|
|
645
|
+
throw new DeploySDKError(
|
|
646
|
+
"API_ERROR" /* API_ERROR */,
|
|
647
|
+
error.message || "Failed to submit redeem order"
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
const result = await response.json();
|
|
651
|
+
return {
|
|
652
|
+
success: true,
|
|
653
|
+
orderId: order.orderId,
|
|
654
|
+
txHash: result.txHash,
|
|
655
|
+
status: result.status
|
|
656
|
+
};
|
|
657
|
+
} catch (error) {
|
|
658
|
+
if (error instanceof DeploySDKError) throw error;
|
|
659
|
+
throw new DeploySDKError(
|
|
660
|
+
"API_ERROR" /* API_ERROR */,
|
|
661
|
+
"Failed to submit redeem order",
|
|
662
|
+
error
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
async redeem(token, redeemAmount, collateralAsset, collateralAmount, options) {
|
|
667
|
+
try {
|
|
668
|
+
const order = await this.createOrder(
|
|
669
|
+
token,
|
|
670
|
+
redeemAmount,
|
|
671
|
+
collateralAsset,
|
|
672
|
+
collateralAmount,
|
|
673
|
+
options
|
|
674
|
+
);
|
|
675
|
+
const signature = await this.signOrder(order);
|
|
676
|
+
return await this.submitOrder(order, signature);
|
|
677
|
+
} catch (error) {
|
|
678
|
+
if (error instanceof DeploySDKError) throw error;
|
|
679
|
+
throw new DeploySDKError(
|
|
680
|
+
"REDEEM_FAILED" /* REDEEM_FAILED */,
|
|
681
|
+
"Failed to redeem tokens",
|
|
682
|
+
error
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
async estimateGas(token, redeemAmount, collateralAsset, collateralAmount, signature) {
|
|
687
|
+
const minterContract = new Contract4(
|
|
688
|
+
token.mintingContract,
|
|
689
|
+
MINTER_ABI,
|
|
690
|
+
this.sdk.provider
|
|
691
|
+
);
|
|
692
|
+
const order = {
|
|
693
|
+
order_id: uuidv42(),
|
|
694
|
+
order_type: 1,
|
|
695
|
+
expiry: Math.floor(Date.now() / 1e3) + 300,
|
|
696
|
+
nonce: Date.now(),
|
|
697
|
+
benefactor: await this.sdk.getAddress(),
|
|
698
|
+
beneficiary: await this.sdk.getAddress(),
|
|
699
|
+
collateral_asset: collateralAsset,
|
|
700
|
+
collateral_amount: collateralAmount.toString(),
|
|
701
|
+
dAsset_amount: redeemAmount.toString()
|
|
702
|
+
};
|
|
703
|
+
try {
|
|
704
|
+
const gasEstimate = await minterContract.estimateGas.redeem(
|
|
705
|
+
order,
|
|
706
|
+
{ signature_type: 0, signature_bytes: signature }
|
|
707
|
+
);
|
|
708
|
+
return gasEstimate.toBigInt();
|
|
709
|
+
} catch (error) {
|
|
710
|
+
throw new DeploySDKError(
|
|
711
|
+
"TRANSACTION_FAILED" /* TRANSACTION_FAILED */,
|
|
712
|
+
"Failed to estimate gas for redeem",
|
|
713
|
+
error
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
export {
|
|
720
|
+
ErrorCode,
|
|
721
|
+
DeploySDKError,
|
|
722
|
+
CONTRACT_ADDRESSES,
|
|
723
|
+
COLLATERAL_ASSETS,
|
|
724
|
+
STAKE_TOKENS,
|
|
725
|
+
EIP712_TYPES_ORDER,
|
|
726
|
+
ERC4626_ABI,
|
|
727
|
+
ERC20_ABI,
|
|
728
|
+
MINTER_ABI,
|
|
729
|
+
MintModule,
|
|
730
|
+
StakeModule,
|
|
731
|
+
UnstakeModule,
|
|
732
|
+
RedeemModule
|
|
733
|
+
};
|