@trusset/sdk 0.1.0 → 0.5.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.
- package/README.md +175 -171
- package/dist/base-BYEauvBZ.d.mts +111 -0
- package/dist/base-zODt0Kf5.d.ts +111 -0
- package/dist/chunk-2KHUPARH.js +2260 -0
- package/dist/chunk-3YF42MHV.mjs +678 -0
- package/dist/chunk-4MNJZAXE.mjs +2260 -0
- package/dist/chunk-7HFMHYRA.js +107 -0
- package/dist/chunk-ACXAKW5E.js +916 -0
- package/dist/chunk-AI4ZNJLR.js +678 -0
- package/dist/chunk-C7CANC53.mjs +208 -0
- package/dist/chunk-DQCX2GYT.mjs +107 -0
- package/dist/chunk-GWMT6UU7.js +208 -0
- package/dist/chunk-OQG3PEF3.mjs +916 -0
- package/dist/flow-Cz8A09rn.d.mts +171 -0
- package/dist/flow-Cz8A09rn.d.ts +171 -0
- package/dist/index-DKUSzkBN.d.ts +956 -0
- package/dist/index-Dl4U8vUM.d.mts +956 -0
- package/dist/index.d.mts +39 -448
- package/dist/index.d.ts +39 -448
- package/dist/index.js +264 -534
- package/dist/index.mjs +249 -483
- package/dist/modules/customers/index.d.mts +2 -0
- package/dist/modules/customers/index.d.ts +2 -0
- package/dist/modules/customers/index.js +17 -0
- package/dist/modules/customers/index.mjs +17 -0
- package/dist/modules/lending/index.d.mts +6768 -0
- package/dist/modules/lending/index.d.ts +6768 -0
- package/dist/modules/lending/index.js +44 -0
- package/dist/modules/lending/index.mjs +44 -0
- package/dist/modules/tokens/index.d.mts +476 -0
- package/dist/modules/tokens/index.d.ts +476 -0
- package/dist/modules/tokens/index.js +8 -0
- package/dist/modules/tokens/index.mjs +8 -0
- package/package.json +71 -35
|
@@ -0,0 +1,2260 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BUILD,
|
|
3
|
+
CONFIRM,
|
|
4
|
+
CONFIRM_ONCE,
|
|
5
|
+
DOCUMENT_TIMEOUT_MS,
|
|
6
|
+
LENDING_BASE,
|
|
7
|
+
LendingArea,
|
|
8
|
+
LendingContext,
|
|
9
|
+
compact,
|
|
10
|
+
enc
|
|
11
|
+
} from "./chunk-DQCX2GYT.mjs";
|
|
12
|
+
import {
|
|
13
|
+
ConfirmationError,
|
|
14
|
+
TrussetError,
|
|
15
|
+
ValidationError,
|
|
16
|
+
assertInputIsNotOptions,
|
|
17
|
+
attachTransactions,
|
|
18
|
+
checkSignedPrice,
|
|
19
|
+
clampInt,
|
|
20
|
+
confirmExecuted,
|
|
21
|
+
execute,
|
|
22
|
+
isConfirmOptions,
|
|
23
|
+
isSignOptions,
|
|
24
|
+
optionalAmount,
|
|
25
|
+
optionalBoolean,
|
|
26
|
+
optionalInt,
|
|
27
|
+
optionalOneOf,
|
|
28
|
+
optionalWallet,
|
|
29
|
+
planOf,
|
|
30
|
+
preflightChain,
|
|
31
|
+
requireAmount,
|
|
32
|
+
requireBoolean,
|
|
33
|
+
requireHash,
|
|
34
|
+
requireInt,
|
|
35
|
+
requireIntegerString,
|
|
36
|
+
requireMarketId,
|
|
37
|
+
requireOneOf,
|
|
38
|
+
requirePositiveId,
|
|
39
|
+
requireRecordId,
|
|
40
|
+
requireSigner,
|
|
41
|
+
requireString,
|
|
42
|
+
requireTxHash,
|
|
43
|
+
requireWallet,
|
|
44
|
+
sameAddress,
|
|
45
|
+
signTypedData,
|
|
46
|
+
signerAddress,
|
|
47
|
+
splitSignature,
|
|
48
|
+
walletOf
|
|
49
|
+
} from "./chunk-3YF42MHV.mjs";
|
|
50
|
+
|
|
51
|
+
// src/modules/lending/markets.ts
|
|
52
|
+
var PRICE_SOURCES = ["MARKET", "NAV", "ORACLE", "FIXED"];
|
|
53
|
+
var COLLATERAL_MODES = ["FREEZE", "CUSTODY"];
|
|
54
|
+
var RATE_MODES = ["CURVE", "CLAMPED", "FIXED"];
|
|
55
|
+
var UPGRADE_TARGETS = ["market", "rateModel", "router", "insuranceFund", "oracle"];
|
|
56
|
+
var UPGRADE_ACTIONS = ["propose", "cancel", "execute"];
|
|
57
|
+
var PRICE_HALT_ACTIONS = ["halt", "lift"];
|
|
58
|
+
var MAX_PRICE_HALT_SECONDS = 86400;
|
|
59
|
+
var HISTORY_RANGES = ["24h", "7d", "30d", "90d", "1y", "all"];
|
|
60
|
+
var HISTORY_RESOLUTIONS = ["hour", "day"];
|
|
61
|
+
var STATEMENT_CATEGORIES = ["config", "roles", "encumbrances", "loans", "liquidations", "auctions"];
|
|
62
|
+
var CONFIGURE_ACTIONS = [
|
|
63
|
+
"authorizeOnRouter",
|
|
64
|
+
"grantLiquidatorRole",
|
|
65
|
+
"upgradeMarket",
|
|
66
|
+
"upgradeRouter",
|
|
67
|
+
"verifyAdapterAsHolder",
|
|
68
|
+
"verifyRouterAsHolder",
|
|
69
|
+
"authorizeAdapterOnToken",
|
|
70
|
+
"authorizeRouterOnToken",
|
|
71
|
+
"authorizeVenueOnToken",
|
|
72
|
+
"verifyVenueAsHolder",
|
|
73
|
+
"setInsuranceFund",
|
|
74
|
+
"grantAgentRoleToAdapter",
|
|
75
|
+
"authorizeRelayerOnOracle"
|
|
76
|
+
];
|
|
77
|
+
var HOLDER_ACTIONS = ["verifyAdapterAsHolder", "verifyRouterAsHolder", "verifyVenueAsHolder"];
|
|
78
|
+
var PROFILE_KEY_RE = /^[A-Za-z0-9_-]{1,60}$/;
|
|
79
|
+
var ACCESS_LIST_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
80
|
+
var PRICE_RE = /^\d+(\.\d{1,18})?$/;
|
|
81
|
+
var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
82
|
+
var CLAIM_SIGNATURE_RE = /^0x[a-fA-F0-9]{130}$/;
|
|
83
|
+
function optionalProfileKey(value, field) {
|
|
84
|
+
if (value === void 0 || value === null) return void 0;
|
|
85
|
+
const s = typeof value === "string" ? value.trim() : "";
|
|
86
|
+
if (!PROFILE_KEY_RE.test(s)) throw new ValidationError(`${field} must be a verification profile key (letters, digits, underscores and hyphens, at most 60 characters)`);
|
|
87
|
+
return s;
|
|
88
|
+
}
|
|
89
|
+
function optionalAccessListId(value, field) {
|
|
90
|
+
if (value === void 0 || value === null) return void 0;
|
|
91
|
+
const s = typeof value === "string" ? value.trim() : "";
|
|
92
|
+
if (!ACCESS_LIST_ID_RE.test(s)) throw new ValidationError(`${field} must be a whitelist id (letters, digits, underscores and hyphens, at most 64 characters)`);
|
|
93
|
+
return s;
|
|
94
|
+
}
|
|
95
|
+
function optionalRegister(value, field) {
|
|
96
|
+
if (value === void 0) return void 0;
|
|
97
|
+
if (value === null || value === "") return null;
|
|
98
|
+
return requireWallet(value, field, { allowZero: true });
|
|
99
|
+
}
|
|
100
|
+
function rateOfferingCheck(input) {
|
|
101
|
+
const floorGiven = input.rateFloorBps !== void 0 && input.rateFloorBps !== null;
|
|
102
|
+
const capGiven = input.rateCapBps !== void 0 && input.rateCapBps !== null;
|
|
103
|
+
const fixedGiven = input.fixedRateBps !== void 0 && input.fixedRateBps !== null;
|
|
104
|
+
if (input.rateMode === "FIXED") {
|
|
105
|
+
if (!fixedGiven) throw new ValidationError("A FIXED rate mode requires fixedRateBps");
|
|
106
|
+
if (floorGiven || capGiven) throw new ValidationError("A FIXED rate mode takes fixedRateBps only; rateFloorBps and rateCapBps belong to CLAMPED");
|
|
107
|
+
} else if (input.rateMode === "CLAMPED") {
|
|
108
|
+
if (!capGiven) throw new ValidationError("A CLAMPED rate mode requires rateCapBps");
|
|
109
|
+
if (fixedGiven) throw new ValidationError("A CLAMPED rate mode takes rateFloorBps and rateCapBps only; fixedRateBps belongs to FIXED");
|
|
110
|
+
} else if (floorGiven || capGiven || fixedGiven) {
|
|
111
|
+
throw new ValidationError("Rate bounds need a rateMode of CLAMPED or FIXED; the CURVE default takes none");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function terminalDueDateOf(value) {
|
|
115
|
+
if (value === void 0 || value === null) return void 0;
|
|
116
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
|
|
117
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
118
|
+
throw new ValidationError("terminalDueDate must be unix seconds or an ISO date string");
|
|
119
|
+
}
|
|
120
|
+
function deployBody(input) {
|
|
121
|
+
if (!input || typeof input !== "object") throw new ValidationError("deploy needs the market parameters");
|
|
122
|
+
if (input.operatorShareBps !== void 0) {
|
|
123
|
+
throw new ValidationError("operatorShareBps is no longer accepted: every market pays its operator and distributors the fixed shares the factory sets");
|
|
124
|
+
}
|
|
125
|
+
const priceSource = optionalOneOf(input.priceSource, "priceSource", PRICE_SOURCES);
|
|
126
|
+
const oracleAddress = optionalWallet(input.oracleAddress, "oracleAddress");
|
|
127
|
+
let initialPrice;
|
|
128
|
+
if (input.initialPrice !== void 0) {
|
|
129
|
+
initialPrice = requireAmount(input.initialPrice, "initialPrice");
|
|
130
|
+
if (!PRICE_RE.test(initialPrice)) throw new ValidationError("initialPrice must have at most 18 decimal places");
|
|
131
|
+
}
|
|
132
|
+
if (priceSource === "ORACLE") {
|
|
133
|
+
if (!oracleAddress) throw new ValidationError("The ORACLE price source needs oracleAddress: the external feed the market reads prices from");
|
|
134
|
+
} else {
|
|
135
|
+
if (oracleAddress) throw new ValidationError("oracleAddress only applies to the ORACLE price source");
|
|
136
|
+
if (initialPrice === void 0) throw new ValidationError("initialPrice is required for the NAV, MARKET and FIXED price sources");
|
|
137
|
+
}
|
|
138
|
+
const rateMode = optionalOneOf(input.rateMode, "rateMode", RATE_MODES);
|
|
139
|
+
const body = {
|
|
140
|
+
collateralTokenAddress: requireWallet(input.collateralTokenAddress, "collateralTokenAddress"),
|
|
141
|
+
priceSource,
|
|
142
|
+
initialPrice,
|
|
143
|
+
oracleAddress,
|
|
144
|
+
mode: optionalOneOf(input.mode, "mode", COLLATERAL_MODES),
|
|
145
|
+
borrowAssetAddress: optionalWallet(input.borrowAssetAddress, "borrowAssetAddress"),
|
|
146
|
+
identityRegistry: optionalWallet(input.identityRegistry, "identityRegistry", { allowZero: true }),
|
|
147
|
+
providerRegistry: optionalWallet(input.providerRegistry, "providerRegistry", { allowZero: true }),
|
|
148
|
+
identityRegistryProfile: optionalProfileKey(input.identityRegistryProfile, "identityRegistryProfile"),
|
|
149
|
+
providerRegistryProfile: optionalProfileKey(input.providerRegistryProfile, "providerRegistryProfile"),
|
|
150
|
+
identityRegistryAccessList: optionalAccessListId(input.identityRegistryAccessList, "identityRegistryAccessList"),
|
|
151
|
+
providerRegistryAccessList: optionalAccessListId(input.providerRegistryAccessList, "providerRegistryAccessList"),
|
|
152
|
+
attestIdentifiedMembers: optionalBoolean(input.attestIdentifiedMembers, "attestIdentifiedMembers"),
|
|
153
|
+
fallbackDecimals: optionalInt(input.fallbackDecimals, "fallbackDecimals", 0, 36),
|
|
154
|
+
openToAnyCurator: optionalBoolean(input.openToAnyCurator, "openToAnyCurator"),
|
|
155
|
+
acknowledgeFixedPriceMismatch: optionalBoolean(input.acknowledgeFixedPriceMismatch, "acknowledgeFixedPriceMismatch"),
|
|
156
|
+
rateMode,
|
|
157
|
+
rateFloorBps: optionalInt(input.rateFloorBps, "rateFloorBps", 0, 5e4),
|
|
158
|
+
rateCapBps: optionalInt(input.rateCapBps, "rateCapBps", 1, 5e4),
|
|
159
|
+
fixedRateBps: optionalInt(input.fixedRateBps, "fixedRateBps", 1, 5e4),
|
|
160
|
+
maxLoanDurationDays: optionalInt(input.maxLoanDurationDays, "maxLoanDurationDays", 1, 3650),
|
|
161
|
+
termGracePeriodDays: optionalInt(input.termGracePeriodDays, "termGracePeriodDays", 1, 30),
|
|
162
|
+
terminalDueDate: terminalDueDateOf(input.terminalDueDate)
|
|
163
|
+
};
|
|
164
|
+
if (input.candidates !== void 0) {
|
|
165
|
+
if (!Array.isArray(input.candidates) || input.candidates.length > 20) throw new ValidationError("candidates must be a list of at most 20 wallet addresses");
|
|
166
|
+
body.candidates = input.candidates.map((c, i) => requireWallet(c, `candidates[${i}]`));
|
|
167
|
+
}
|
|
168
|
+
if (rateMode === "FIXED" && body.maxLoanDurationDays === void 0) {
|
|
169
|
+
throw new ValidationError("A FIXED rate mode requires a term offering: set maxLoanDurationDays");
|
|
170
|
+
}
|
|
171
|
+
rateOfferingCheck(input);
|
|
172
|
+
if (body.maxLoanDurationDays === void 0 && body.terminalDueDate === void 0 && body.termGracePeriodDays !== void 0) {
|
|
173
|
+
throw new ValidationError("A grace period needs loan terms: set maxLoanDurationDays or terminalDueDate as well");
|
|
174
|
+
}
|
|
175
|
+
return compact(body);
|
|
176
|
+
}
|
|
177
|
+
function deployConfirmFields(source) {
|
|
178
|
+
if (!source) return {};
|
|
179
|
+
const out = {};
|
|
180
|
+
for (const key of ["collateralTokenAddress", "mode", "priceSource", "borrowAssetAddress", "identityRegistry"]) {
|
|
181
|
+
const value = source[key];
|
|
182
|
+
if (value !== void 0 && value !== null && value !== "") out[key] = value;
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
function configBody(config) {
|
|
187
|
+
if (!config || typeof config !== "object") throw new ValidationError("config must be the full 12-member risk configuration");
|
|
188
|
+
const collateralFactor = requireInt(config.collateralFactor, "config.collateralFactor", 1e3, 9e3);
|
|
189
|
+
const liquidationThreshold = requireInt(config.liquidationThreshold, "config.liquidationThreshold", 1e3, 9500);
|
|
190
|
+
if (liquidationThreshold <= collateralFactor) throw new ValidationError("config.liquidationThreshold must be strictly greater than config.collateralFactor; the market contract refuses anything else");
|
|
191
|
+
const useDutchAuction = requireBoolean(config.useDutchAuction, "config.useDutchAuction");
|
|
192
|
+
const auctionStartPremium = requireInt(config.auctionStartPremium, "config.auctionStartPremium", 1e4, 2e4);
|
|
193
|
+
const auctionMinPremium = requireInt(config.auctionMinPremium, "config.auctionMinPremium", 8e3, 1e4);
|
|
194
|
+
if (useDutchAuction && auctionStartPremium <= auctionMinPremium) throw new ValidationError("config.auctionStartPremium must be strictly greater than config.auctionMinPremium while auctions are on");
|
|
195
|
+
return {
|
|
196
|
+
collateralFactor,
|
|
197
|
+
liquidationThreshold,
|
|
198
|
+
liquidationPenalty: requireInt(config.liquidationPenalty, "config.liquidationPenalty", 0, 5e3),
|
|
199
|
+
protocolFeeRate: requireInt(config.protocolFeeRate, "config.protocolFeeRate", 0, 1e4),
|
|
200
|
+
minBorrowAmount: requireAmount(config.minBorrowAmount, "config.minBorrowAmount", { allowZero: true }),
|
|
201
|
+
closeFactor: requireInt(config.closeFactor, "config.closeFactor", 1e3, 5e3),
|
|
202
|
+
maxTotalCollateral: requireAmount(config.maxTotalCollateral, "config.maxTotalCollateral", { allowZero: true }),
|
|
203
|
+
maxUserCollateral: requireAmount(config.maxUserCollateral, "config.maxUserCollateral", { allowZero: true }),
|
|
204
|
+
useDutchAuction,
|
|
205
|
+
auctionDuration: requireInt(config.auctionDuration, "config.auctionDuration", 600, 86400),
|
|
206
|
+
auctionStartPremium,
|
|
207
|
+
auctionMinPremium
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function identityGatesBody(input) {
|
|
211
|
+
if (!input || typeof input !== "object") throw new ValidationError("setIdentityGates needs the registers to set");
|
|
212
|
+
return compact({
|
|
213
|
+
borrowerRegistry: optionalRegister(input.borrowerRegistry, "borrowerRegistry"),
|
|
214
|
+
providerRegistry: optionalRegister(input.providerRegistry, "providerRegistry"),
|
|
215
|
+
borrowerProfile: optionalProfileKey(input.borrowerProfile, "borrowerProfile"),
|
|
216
|
+
providerProfile: optionalProfileKey(input.providerProfile, "providerProfile"),
|
|
217
|
+
borrowerAccessList: optionalAccessListId(input.borrowerAccessList, "borrowerAccessList"),
|
|
218
|
+
providerAccessList: optionalAccessListId(input.providerAccessList, "providerAccessList"),
|
|
219
|
+
attestIdentifiedMembers: optionalBoolean(input.attestIdentifiedMembers, "attestIdentifiedMembers")
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function configureBody(input) {
|
|
223
|
+
const action = optionalOneOf(input.action, "action", CONFIGURE_ACTIONS);
|
|
224
|
+
const body = { action, acknowledgeFeeModelImpact: optionalBoolean(input.acknowledgeFeeModelImpact, "acknowledgeFeeModelImpact") };
|
|
225
|
+
if (action && HOLDER_ACTIONS.includes(action)) {
|
|
226
|
+
body.identityAddress = optionalWallet(input.identityAddress, "identityAddress");
|
|
227
|
+
body.country = optionalInt(input.country, "country", 0, 65535);
|
|
228
|
+
body.signerAddress = optionalWallet(input.signerAddress, "signerAddress");
|
|
229
|
+
if (input.claimSignatures !== void 0) {
|
|
230
|
+
if (!Array.isArray(input.claimSignatures) || input.claimSignatures.length > 10) throw new ValidationError("claimSignatures must list at most 10 signed claims");
|
|
231
|
+
body.claimSignatures = input.claimSignatures.map((c, i) => {
|
|
232
|
+
if (typeof c?.signature !== "string" || !CLAIM_SIGNATURE_RE.test(c.signature)) throw new ValidationError(`claimSignatures[${i}].signature must be a 65-byte hex signature`);
|
|
233
|
+
if (c.claimType !== void 0 && c.claimType !== null && (typeof c.claimType !== "string" || c.claimType.length > 66)) throw new ValidationError(`claimSignatures[${i}].claimType must be at most 66 characters`);
|
|
234
|
+
let issuedAt;
|
|
235
|
+
if (c.issuedAt !== void 0 && c.issuedAt !== null) {
|
|
236
|
+
const n = typeof c.issuedAt === "string" && /^\d+$/.test(c.issuedAt.trim()) ? Number(c.issuedAt.trim()) : c.issuedAt;
|
|
237
|
+
if (typeof n !== "number" || !Number.isSafeInteger(n) || n <= 0) throw new ValidationError(`claimSignatures[${i}].issuedAt must be the unix time in seconds the holder plan's message carried`);
|
|
238
|
+
issuedAt = n;
|
|
239
|
+
}
|
|
240
|
+
return compact({ walletAddress: requireWallet(c.walletAddress, `claimSignatures[${i}].walletAddress`), signature: c.signature, claimType: c.claimType ?? void 0, issuedAt });
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (input.identities !== void 0) {
|
|
244
|
+
const entries = Object.entries(input.identities ?? {});
|
|
245
|
+
if (entries.length > 4) throw new ValidationError("identities must map at most 4 wallets to their ONCHAINID");
|
|
246
|
+
body.identities = Object.fromEntries(entries.map(([w, id]) => [requireWallet(w, "identities wallet"), requireWallet(id, "identities identity")]));
|
|
247
|
+
}
|
|
248
|
+
} else if (input.signerAddress !== void 0 || input.identityAddress !== void 0 || input.country !== void 0 || input.claimSignatures !== void 0 || input.identities !== void 0) {
|
|
249
|
+
throw new ValidationError("identityAddress, country, signerAddress, claimSignatures and identities only apply to the verify*AsHolder actions");
|
|
250
|
+
}
|
|
251
|
+
return compact(body);
|
|
252
|
+
}
|
|
253
|
+
var LendingMarkets = class extends LendingArea {
|
|
254
|
+
path(marketId, rest = "") {
|
|
255
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}${rest ? `/${rest}` : ""}`;
|
|
256
|
+
}
|
|
257
|
+
async list(options = {}) {
|
|
258
|
+
return this.http.get(`${LENDING_BASE}/markets`, { includeOnChain: optionalBoolean(options.includeOnChain, "includeOnChain") ? "true" : void 0 });
|
|
259
|
+
}
|
|
260
|
+
async get(marketId) {
|
|
261
|
+
return this.http.get(this.path(marketId));
|
|
262
|
+
}
|
|
263
|
+
async metrics(marketId) {
|
|
264
|
+
return this.http.get(this.path(marketId, "metrics"));
|
|
265
|
+
}
|
|
266
|
+
async config(marketId) {
|
|
267
|
+
return this.http.get(this.path(marketId, "config"));
|
|
268
|
+
}
|
|
269
|
+
async supplyCaps(marketId) {
|
|
270
|
+
return this.http.get(this.path(marketId, "supply-caps"));
|
|
271
|
+
}
|
|
272
|
+
async maxBorrow(marketId, collateralAmount) {
|
|
273
|
+
return this.http.get(this.path(marketId, "max-borrow"), { collateralAmount: requireAmount(collateralAmount, "collateralAmount") });
|
|
274
|
+
}
|
|
275
|
+
async transactions(marketId, options = {}) {
|
|
276
|
+
return this.http.get(this.path(marketId, "transactions"), {
|
|
277
|
+
limit: clampInt(options.limit, 1, 200, 50),
|
|
278
|
+
offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0)
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
async history(marketId, options = {}) {
|
|
282
|
+
return this.http.get(this.path(marketId, "history"), {
|
|
283
|
+
range: optionalOneOf(options.range, "range", HISTORY_RANGES),
|
|
284
|
+
resolution: optionalOneOf(options.resolution, "resolution", HISTORY_RESOLUTIONS)
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
async indexStatus(marketId) {
|
|
288
|
+
return this.http.get(this.path(marketId, "index-status"));
|
|
289
|
+
}
|
|
290
|
+
async lifecycleStatement(marketId, options = {}) {
|
|
291
|
+
let categories;
|
|
292
|
+
if (options.categories !== void 0) {
|
|
293
|
+
if (!Array.isArray(options.categories) || options.categories.length === 0) throw new ValidationError("categories must be a non-empty list");
|
|
294
|
+
categories = options.categories.map((c) => requireOneOf(c, "categories", STATEMENT_CATEGORIES)).join(",");
|
|
295
|
+
}
|
|
296
|
+
let loanId;
|
|
297
|
+
if (options.loanId !== void 0) {
|
|
298
|
+
loanId = String(options.loanId);
|
|
299
|
+
if (!/^\d{1,18}$/.test(loanId) || /^0+$/.test(loanId)) throw new ValidationError("loanId must be a positive whole number of at most 18 digits");
|
|
300
|
+
}
|
|
301
|
+
for (const [field, value] of [["from", options.from], ["to", options.to]]) {
|
|
302
|
+
if (value !== void 0 && (typeof value !== "string" || !DATE_RE.test(value))) throw new ValidationError(`${field} must be a date written as YYYY-MM-DD`);
|
|
303
|
+
}
|
|
304
|
+
return this.http.getBinary(this.path(marketId, "lifecycle-statement"), {
|
|
305
|
+
categories,
|
|
306
|
+
loanId,
|
|
307
|
+
from: options.from,
|
|
308
|
+
to: options.to,
|
|
309
|
+
allowStale: optionalBoolean(options.allowStale, "allowStale") ? "true" : void 0
|
|
310
|
+
}, { timeoutMs: DOCUMENT_TIMEOUT_MS });
|
|
311
|
+
}
|
|
312
|
+
async borrowAssets() {
|
|
313
|
+
return this.http.get(`${LENDING_BASE}/markets/borrow-assets`);
|
|
314
|
+
}
|
|
315
|
+
async savedStablecoins() {
|
|
316
|
+
return this.http.get("/instances/api/saved-stablecoins");
|
|
317
|
+
}
|
|
318
|
+
async forgetStablecoin(address) {
|
|
319
|
+
return this.http.delete(`/instances/api/saved-stablecoins/${requireWallet(address, "address")}`);
|
|
320
|
+
}
|
|
321
|
+
async deploymentOptions() {
|
|
322
|
+
return this.http.get(`${LENDING_BASE}/markets/deployment-options`);
|
|
323
|
+
}
|
|
324
|
+
async configurationStatus(marketId) {
|
|
325
|
+
return this.http.get(this.path(marketId, "configuration-status"));
|
|
326
|
+
}
|
|
327
|
+
async setupSteps(marketId) {
|
|
328
|
+
return this.http.get(this.path(marketId, "setup-steps"));
|
|
329
|
+
}
|
|
330
|
+
async upgradeStatus(marketId) {
|
|
331
|
+
return this.http.get(this.path(marketId, "upgrade"));
|
|
332
|
+
}
|
|
333
|
+
async guardians(marketId, address) {
|
|
334
|
+
return this.http.get(this.path(marketId, "guardians"), { address: optionalWallet(address, "address") });
|
|
335
|
+
}
|
|
336
|
+
async priceHalt(marketId) {
|
|
337
|
+
return this.http.get(this.path(marketId, "oracle-halt"));
|
|
338
|
+
}
|
|
339
|
+
async thresholdCooldown(marketId) {
|
|
340
|
+
return this.http.get(this.path(marketId, "threshold-cooldown"));
|
|
341
|
+
}
|
|
342
|
+
async setPriceHalt(marketId, input, options) {
|
|
343
|
+
const path = this.path(marketId, "oracle-halt");
|
|
344
|
+
const action = requireOneOf(input?.action, "action", PRICE_HALT_ACTIONS);
|
|
345
|
+
if (action === "lift" && input.durationSeconds !== void 0) throw new ValidationError("durationSeconds applies to a halt only; a lift ends the running halt");
|
|
346
|
+
const durationSeconds = action === "halt" ? requireInt(input.durationSeconds, "durationSeconds", 1, MAX_PRICE_HALT_SECONDS) : void 0;
|
|
347
|
+
const signerAddress2 = isSignOptions(options) ? await walletOf(options, void 0, "signerAddress") : void 0;
|
|
348
|
+
return this.twoLeg(options, {
|
|
349
|
+
build: () => this.http.post(path, compact({ action, durationSeconds, signerAddress: signerAddress2 }), BUILD),
|
|
350
|
+
confirm: (txHash) => this.http.post(path, { action, txHash }, CONFIRM),
|
|
351
|
+
check: async (prepared, signer) => {
|
|
352
|
+
if (prepared.requiredSigner && !sameAddress(prepared.requiredSigner, signer)) {
|
|
353
|
+
throw new ValidationError(`Lifting the price halt must be signed by the oracle owner ${prepared.requiredSigner}, but the signer is ${signer}. Nothing was signed`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async setPaused(marketId, paused, options) {
|
|
359
|
+
const path = this.path(marketId, "pause-state");
|
|
360
|
+
const state = requireBoolean(paused, "paused");
|
|
361
|
+
const signerAddress2 = isSignOptions(options) ? await walletOf(options, void 0, "signerAddress") : void 0;
|
|
362
|
+
return this.twoLeg(options, {
|
|
363
|
+
build: () => this.http.post(path, compact({ paused: state, signerAddress: signerAddress2 }), BUILD),
|
|
364
|
+
confirm: (txHash) => this.http.post(path, { paused: state, txHash }, CONFIRM)
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
async identityGates(marketId) {
|
|
368
|
+
return this.http.get(this.path(marketId, "identity-gates"));
|
|
369
|
+
}
|
|
370
|
+
async deploy(input, options) {
|
|
371
|
+
const path = `${LENDING_BASE}/markets/deploy`;
|
|
372
|
+
if (isConfirmOptions(options)) {
|
|
373
|
+
const hints = input ?? {};
|
|
374
|
+
const fields = compact({
|
|
375
|
+
collateralTokenAddress: optionalWallet(hints.collateralTokenAddress, "collateralTokenAddress"),
|
|
376
|
+
mode: optionalOneOf(hints.mode, "mode", COLLATERAL_MODES),
|
|
377
|
+
priceSource: optionalOneOf(hints.priceSource, "priceSource", PRICE_SOURCES),
|
|
378
|
+
borrowAssetAddress: optionalWallet(hints.borrowAssetAddress, "borrowAssetAddress"),
|
|
379
|
+
identityRegistry: optionalWallet(hints.identityRegistry, "identityRegistry", { allowZero: true })
|
|
380
|
+
});
|
|
381
|
+
return this.http.post(path, { ...fields, txHash: requireTxHash(options.txHash) }, CONFIRM);
|
|
382
|
+
}
|
|
383
|
+
const body = deployBody(input);
|
|
384
|
+
return this.twoLeg(options, {
|
|
385
|
+
build: () => this.http.post(path, body, BUILD),
|
|
386
|
+
confirm: (txHash, prepared) => this.http.post(
|
|
387
|
+
path,
|
|
388
|
+
{ ...deployConfirmFields(prepared?.confirmWith?.body ?? body), txHash },
|
|
389
|
+
CONFIRM
|
|
390
|
+
)
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
async updateConfig(marketId, config, options) {
|
|
394
|
+
const path = this.path(marketId, "config");
|
|
395
|
+
const validated = configBody(config);
|
|
396
|
+
if (isSignOptions(options)) {
|
|
397
|
+
await this.assertSignerHoldsAny(marketId, await walletOf(options, void 0, "signer"), ["MARKET_ISSUER"]);
|
|
398
|
+
}
|
|
399
|
+
return this.twoLeg(options, {
|
|
400
|
+
build: () => this.http.post(path, { config: validated }, BUILD),
|
|
401
|
+
confirm: (txHash) => this.http.post(path, { config: validated, txHash }, CONFIRM)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async configure(marketId, input, options) {
|
|
405
|
+
assertInputIsNotOptions(input, "configure(marketId, { action }, { signer })");
|
|
406
|
+
const buildPath = this.path(marketId, "configure");
|
|
407
|
+
const confirmPath = this.path(marketId, "confirm-configuration");
|
|
408
|
+
const confirm = (txHash, action) => this.http.post(confirmPath, compact({ txHash, action }), CONFIRM);
|
|
409
|
+
if (isConfirmOptions(options)) {
|
|
410
|
+
const action = optionalOneOf(input?.action, "action", CONFIGURE_ACTIONS);
|
|
411
|
+
return confirm(requireTxHash(options.txHash), action);
|
|
412
|
+
}
|
|
413
|
+
const full = { ...input ?? {} };
|
|
414
|
+
if (!isSignOptions(options)) {
|
|
415
|
+
return this.http.post(buildPath, configureBody(full), BUILD);
|
|
416
|
+
}
|
|
417
|
+
if (!full.action) {
|
|
418
|
+
throw new ValidationError("Pass an action to configure with a signer: without one the API lists every outstanding step, and each step is signed by its own authority");
|
|
419
|
+
}
|
|
420
|
+
if (HOLDER_ACTIONS.includes(full.action)) {
|
|
421
|
+
full.signerAddress = await walletOf(options, full.signerAddress ? requireWallet(full.signerAddress, "signerAddress") : void 0, "signerAddress");
|
|
422
|
+
}
|
|
423
|
+
await preflightChain(this.ctx, options);
|
|
424
|
+
const prepared = await this.http.post(buildPath, configureBody(full), BUILD);
|
|
425
|
+
if (prepared.holderPlan) {
|
|
426
|
+
throw new ValidationError(`The ${full.action} step needs a holder onboarding plan (${prepared.holderPlan.phase}), which signs claims and registers identities on the token's registry. Prepare it without a signer and run the plan from the onboarding wallet; confirm the registration with configure(marketId, { action }, { txHash })`);
|
|
427
|
+
}
|
|
428
|
+
const entries = Object.entries(prepared.configurationCalldata ?? {});
|
|
429
|
+
if (entries.length === 0) return attachTransactions(prepared, []);
|
|
430
|
+
const transactions = [];
|
|
431
|
+
let result = null;
|
|
432
|
+
for (const [key, entry] of entries) {
|
|
433
|
+
const plan = planOf(entry, { confirmIndex: 0 });
|
|
434
|
+
if (!plan) throw new ValidationError(`The ${key} step carries no transaction the SDK can sign`);
|
|
435
|
+
const executed = await execute(this.ctx, plan, options);
|
|
436
|
+
const action = key === "pokeSnapshot" ? "upgradeMarket" : key;
|
|
437
|
+
const confirmed = await confirmExecuted({ transactions: [...transactions, ...executed.transactions], confirmHash: executed.confirmHash }, (txHash) => confirm(txHash, action));
|
|
438
|
+
transactions.push(...executed.transactions);
|
|
439
|
+
result = confirmed;
|
|
440
|
+
}
|
|
441
|
+
return attachTransactions(result, transactions);
|
|
442
|
+
}
|
|
443
|
+
async upgrade(marketId, input = {}, options) {
|
|
444
|
+
assertInputIsNotOptions(input, "upgrade(marketId, {}, { signer })");
|
|
445
|
+
const path = this.path(marketId, "upgrade");
|
|
446
|
+
const target = optionalOneOf(input?.target, "target", UPGRADE_TARGETS) ?? "market";
|
|
447
|
+
const action = optionalOneOf(input?.action, "action", UPGRADE_ACTIONS);
|
|
448
|
+
const acknowledgeFeeModelImpact = optionalBoolean(input?.acknowledgeFeeModelImpact, "acknowledgeFeeModelImpact");
|
|
449
|
+
return this.twoLeg(options, {
|
|
450
|
+
build: () => this.http.post(path, compact({ target, action, acknowledgeFeeModelImpact }), BUILD),
|
|
451
|
+
confirm: async (txHash, prepared) => {
|
|
452
|
+
const signed = prepared?.upgradeAction ?? action ?? "execute";
|
|
453
|
+
const confirmed = await this.http.post(path, { txHash, target, action: signed }, CONFIRM);
|
|
454
|
+
if (!prepared || signed !== "execute") return confirmed;
|
|
455
|
+
return { ...confirmed, ...compact({ pokeSnapshot: prepared.pokeSnapshot, followUps: prepared.followUps }) };
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async setIdentityGates(marketId, input, options) {
|
|
460
|
+
const path = this.path(marketId, "identity-gates");
|
|
461
|
+
const attest = optionalBoolean(input?.attestIdentifiedMembers, "attestIdentifiedMembers");
|
|
462
|
+
if (isConfirmOptions(options)) {
|
|
463
|
+
return this.http.post(path, compact({ txHash: requireTxHash(options.txHash), attestIdentifiedMembers: attest }), CONFIRM);
|
|
464
|
+
}
|
|
465
|
+
const body = identityGatesBody(input);
|
|
466
|
+
return this.twoLeg(options, {
|
|
467
|
+
build: () => this.http.post(path, body, BUILD),
|
|
468
|
+
confirm: (txHash, prepared) => this.http.post(
|
|
469
|
+
path,
|
|
470
|
+
compact({ txHash, attestIdentifiedMembers: attest === true || prepared?.attestedIdentifiedMembers === true ? true : void 0 }),
|
|
471
|
+
CONFIRM
|
|
472
|
+
)
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/modules/lending/operators.ts
|
|
478
|
+
var ROLE_KEYS = ["MARKET_ADMIN", "MARKET_ISSUER", "MARKET_LIQUIDATOR", "MARKET_GUARDIAN", "ROUTER_ADMIN", "ROUTER_OPERATOR", "ORACLE_SIGNER"];
|
|
479
|
+
var HANDOVER_SCOPES = ["market", "router", "both"];
|
|
480
|
+
var ROLE_ACTIONS = ["grant", "revoke"];
|
|
481
|
+
var REALIZATION_MODES = ["EXCHANGE_SALE", "ISSUER_REDEMPTION", "AUCTION"];
|
|
482
|
+
var BYTES32_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
483
|
+
var COMMITMENT_RE = /^\d+(\.\d{1,18})?$/;
|
|
484
|
+
function optionalBytes32(value, field) {
|
|
485
|
+
if (value === void 0 || value === null) return void 0;
|
|
486
|
+
if (typeof value !== "string" || !BYTES32_RE.test(value)) throw new ValidationError(`${field} must be a 32-byte hex value: 0x followed by 64 hex characters`);
|
|
487
|
+
return value;
|
|
488
|
+
}
|
|
489
|
+
function termsBody(terms) {
|
|
490
|
+
if (!terms || typeof terms !== "object") throw new ValidationError("accept needs the curator terms");
|
|
491
|
+
let liquidityCommitment;
|
|
492
|
+
if (terms.liquidityCommitment !== void 0) {
|
|
493
|
+
liquidityCommitment = requireAmount(terms.liquidityCommitment, "liquidityCommitment", { allowZero: true });
|
|
494
|
+
if (!COMMITMENT_RE.test(liquidityCommitment)) throw new ValidationError("liquidityCommitment must have at most 18 decimal places");
|
|
495
|
+
}
|
|
496
|
+
return compact({
|
|
497
|
+
admin: requireWallet(terms.admin, "admin"),
|
|
498
|
+
issuer: requireWallet(terms.issuer, "issuer"),
|
|
499
|
+
collateralAgent: requireWallet(terms.collateralAgent, "collateralAgent"),
|
|
500
|
+
saleRecipient: requireWallet(terms.saleRecipient, "saleRecipient"),
|
|
501
|
+
liquidationOperator: requireWallet(terms.liquidationOperator, "liquidationOperator"),
|
|
502
|
+
oracleOwner: requireWallet(terms.oracleOwner, "oracleOwner"),
|
|
503
|
+
priceSigner: requireWallet(terms.priceSigner, "priceSigner"),
|
|
504
|
+
realizationMode: requireOneOf(terms.realizationMode, "realizationMode", REALIZATION_MODES),
|
|
505
|
+
identityRegistry: optionalRegister(terms.identityRegistry, "identityRegistry"),
|
|
506
|
+
providerRegistry: optionalRegister(terms.providerRegistry, "providerRegistry"),
|
|
507
|
+
liquidator: optionalRegister(terms.liquidator, "liquidator"),
|
|
508
|
+
vault: optionalRegister(terms.vault, "vault"),
|
|
509
|
+
liquidityCommitment,
|
|
510
|
+
maxFixedRateBps: optionalInt(terms.maxFixedRateBps, "maxFixedRateBps", 1, 5e4),
|
|
511
|
+
claimSalt: optionalBytes32(terms.claimSalt, "claimSalt"),
|
|
512
|
+
identityRegistryProfile: optionalProfileKey(terms.identityRegistryProfile, "identityRegistryProfile"),
|
|
513
|
+
providerRegistryProfile: optionalProfileKey(terms.providerRegistryProfile, "providerRegistryProfile"),
|
|
514
|
+
identityRegistryAccessList: optionalAccessListId(terms.identityRegistryAccessList, "identityRegistryAccessList"),
|
|
515
|
+
providerRegistryAccessList: optionalAccessListId(terms.providerRegistryAccessList, "providerRegistryAccessList"),
|
|
516
|
+
attestIdentifiedMembers: optionalBoolean(terms.attestIdentifiedMembers, "attestIdentifiedMembers"),
|
|
517
|
+
assetDataHash: optionalBytes32(terms.assetDataHash, "assetDataHash"),
|
|
518
|
+
acknowledgeAssetData: optionalBoolean(terms.acknowledgeAssetData, "acknowledgeAssetData")
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
var ROLE_AUTHORITY = {
|
|
522
|
+
MARKET_ADMIN: "MARKET_ADMIN",
|
|
523
|
+
MARKET_ISSUER: "MARKET_ADMIN",
|
|
524
|
+
MARKET_LIQUIDATOR: "MARKET_ADMIN",
|
|
525
|
+
MARKET_GUARDIAN: "MARKET_ADMIN",
|
|
526
|
+
ROUTER_ADMIN: "ROUTER_ADMIN",
|
|
527
|
+
ROUTER_OPERATOR: "ROUTER_ADMIN",
|
|
528
|
+
ORACLE_SIGNER: "ORACLE_OWNER"
|
|
529
|
+
};
|
|
530
|
+
async function assertSignerIs(options, expected, label2) {
|
|
531
|
+
if (typeof expected !== "string") return;
|
|
532
|
+
const actual = await signerAddress(requireSigner(options.signer));
|
|
533
|
+
if (!sameAddress(actual, expected)) {
|
|
534
|
+
throw new ValidationError(`This transaction must be signed by ${expected} (${label2}), but the signer is ${actual}. Nothing was signed`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
var LendingOperators = class extends LendingArea {
|
|
538
|
+
marketPath(marketId, rest) {
|
|
539
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
540
|
+
}
|
|
541
|
+
queuedPath(marketAddress, rest) {
|
|
542
|
+
return `${LENDING_BASE}/markets/queued/${requireWallet(marketAddress, "marketAddress")}/${rest}`;
|
|
543
|
+
}
|
|
544
|
+
async pending(options = {}) {
|
|
545
|
+
return this.http.get(`${LENDING_BASE}/markets/queue/pending`, {
|
|
546
|
+
offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
|
|
547
|
+
limit: clampInt(options.limit, 1, 100, 25)
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
async queued() {
|
|
551
|
+
return this.http.get(`${LENDING_BASE}/markets/queued`);
|
|
552
|
+
}
|
|
553
|
+
async queuedMarket(marketAddress) {
|
|
554
|
+
return this.http.get(this.queuedPath(marketAddress, "details"));
|
|
555
|
+
}
|
|
556
|
+
async operator(marketId) {
|
|
557
|
+
return this.http.get(this.marketPath(marketId, "operator"));
|
|
558
|
+
}
|
|
559
|
+
async handover(marketId) {
|
|
560
|
+
return this.http.get(this.marketPath(marketId, "handover"));
|
|
561
|
+
}
|
|
562
|
+
async nominations(marketId) {
|
|
563
|
+
return this.http.get(this.marketPath(marketId, "nominations"));
|
|
564
|
+
}
|
|
565
|
+
async adminHandover(marketId, input) {
|
|
566
|
+
return this.http.post(this.marketPath(marketId, "admin-handover"), compact({
|
|
567
|
+
successor: requireWallet(input?.successor, "successor"),
|
|
568
|
+
scope: optionalOneOf(input?.scope, "scope", HANDOVER_SCOPES),
|
|
569
|
+
signerAddress: optionalWallet(input?.signerAddress, "signerAddress")
|
|
570
|
+
}), BUILD);
|
|
571
|
+
}
|
|
572
|
+
async accessRole(marketId, address, role) {
|
|
573
|
+
return this.http.get(this.marketPath(marketId, `access-role/${requireWallet(address, "address")}`), {
|
|
574
|
+
role: requireOneOf(role, "role", ROLE_KEYS)
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
async accept(marketAddress, terms, options) {
|
|
578
|
+
const acceptPath = this.queuedPath(marketAddress, "accept");
|
|
579
|
+
const confirmPath = this.queuedPath(marketAddress, "confirm-accept");
|
|
580
|
+
const attest = optionalBoolean(terms?.attestIdentifiedMembers, "attestIdentifiedMembers");
|
|
581
|
+
const confirm = (txHash, adoptionAttested2) => this.http.post(
|
|
582
|
+
confirmPath,
|
|
583
|
+
compact({ txHash, attestIdentifiedMembers: attest === true || adoptionAttested2 ? true : void 0 }),
|
|
584
|
+
CONFIRM
|
|
585
|
+
);
|
|
586
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash), false);
|
|
587
|
+
const body = termsBody(terms);
|
|
588
|
+
if (!isSignOptions(options)) return this.http.post(acceptPath, body, BUILD);
|
|
589
|
+
await preflightChain(this.ctx, options);
|
|
590
|
+
const prepared = await this.http.post(acceptPath, body, BUILD);
|
|
591
|
+
await assertSignerIs(options, prepared.signer, prepared.functionName === "commitCuratorClaim" ? "the wallet the claim commitment names" : "the nominated wallet that takes the lender role");
|
|
592
|
+
const plan = planOf(prepared, { confirmIndex: 0 });
|
|
593
|
+
if (!plan) throw new TrussetError("The acceptance build returned no transaction to sign", "NOTHING_TO_SIGN", 0);
|
|
594
|
+
if ("claimFlow" in prepared && prepared.claimFlow === true) {
|
|
595
|
+
const executed2 = await execute(this.ctx, plan, options);
|
|
596
|
+
return {
|
|
597
|
+
status: "CLAIM_COMMITTED",
|
|
598
|
+
marketAddress: prepared.marketAddress,
|
|
599
|
+
signer: prepared.signer,
|
|
600
|
+
commitment: prepared.commitment,
|
|
601
|
+
claimSalt: prepared.claimSalt,
|
|
602
|
+
claimDelaySeconds: prepared.claimDelaySeconds,
|
|
603
|
+
claimWindowSeconds: prepared.claimWindowSeconds,
|
|
604
|
+
transactions: executed2.transactions
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
const adoption = prepared;
|
|
608
|
+
const adoptionAttested = adoption.attestedIdentifiedMembers === true;
|
|
609
|
+
const executed = await execute(this.ctx, plan, options);
|
|
610
|
+
return confirmExecuted(executed, async (txHash) => ({
|
|
611
|
+
...await confirm(txHash, adoptionAttested),
|
|
612
|
+
...compact({ warnings: adoption.warnings, requiredFollowUp: adoption.requiredFollowUp })
|
|
613
|
+
}));
|
|
614
|
+
}
|
|
615
|
+
async nominate(marketId, candidate, options) {
|
|
616
|
+
return this.nomination(this.marketPath(marketId, "nominations"), candidate, options);
|
|
617
|
+
}
|
|
618
|
+
async withdrawNomination(marketId, candidate, options) {
|
|
619
|
+
return this.nomination(this.marketPath(marketId, "nominations/withdraw"), candidate, options);
|
|
620
|
+
}
|
|
621
|
+
async setAccessRole(marketId, input, options) {
|
|
622
|
+
const body = {
|
|
623
|
+
address: requireWallet(input?.address, "address"),
|
|
624
|
+
action: requireOneOf(input?.action, "action", ROLE_ACTIONS),
|
|
625
|
+
role: requireOneOf(input?.role, "role", ROLE_KEYS)
|
|
626
|
+
};
|
|
627
|
+
return this.roleChange(marketId, this.marketPath(marketId, "access-role"), body, ROLE_AUTHORITY[body.role], options);
|
|
628
|
+
}
|
|
629
|
+
async setLiquidatorRole(marketId, input, options) {
|
|
630
|
+
const body = {
|
|
631
|
+
address: requireWallet(input?.address, "address"),
|
|
632
|
+
action: requireOneOf(input?.action, "action", ROLE_ACTIONS)
|
|
633
|
+
};
|
|
634
|
+
return this.roleChange(marketId, this.marketPath(marketId, "liquidator-role"), body, "MARKET_ADMIN", options);
|
|
635
|
+
}
|
|
636
|
+
async nomination(path, candidate, options) {
|
|
637
|
+
const body = { candidate: requireWallet(candidate, "candidate") };
|
|
638
|
+
if (options === void 0) return this.http.post(path, body, BUILD);
|
|
639
|
+
if (isConfirmOptions(options)) {
|
|
640
|
+
throw new ValidationError("Nominations have no confirm leg: the factory records them, and nominations() reads them back from the chain");
|
|
641
|
+
}
|
|
642
|
+
if (!isSignOptions(options)) throw new ValidationError("Pass { signer } to sign, or no options to get the unsigned transaction");
|
|
643
|
+
await preflightChain(this.ctx, options);
|
|
644
|
+
const prepared = await this.http.post(path, body, BUILD);
|
|
645
|
+
await assertSignerIs(options, prepared.signer, "the market's deployer or the factory owner");
|
|
646
|
+
const plan = planOf(prepared, { confirmIndex: null });
|
|
647
|
+
if (!plan) throw new TrussetError("The nomination build returned no transaction to sign", "NOTHING_TO_SIGN", 0);
|
|
648
|
+
const executed = await execute(this.ctx, plan, options);
|
|
649
|
+
return { transactions: executed.transactions };
|
|
650
|
+
}
|
|
651
|
+
async roleChange(marketId, path, body, authority, options) {
|
|
652
|
+
const confirm = (txHash) => this.http.post(path, { ...body, txHash }, CONFIRM);
|
|
653
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
654
|
+
if (!isSignOptions(options)) return this.http.post(path, body, BUILD);
|
|
655
|
+
await preflightChain(this.ctx, options);
|
|
656
|
+
const prepared = await this.http.post(path, body, BUILD);
|
|
657
|
+
if ("unchanged" in prepared && prepared.unchanged === true) return attachTransactions(prepared, []);
|
|
658
|
+
if (authority === "ORACLE_OWNER") {
|
|
659
|
+
await assertSignerIs(options, prepared.requiredSigner?.wallet, "the oracle owner");
|
|
660
|
+
} else {
|
|
661
|
+
await this.assertSignerRoles(marketId, await signerAddress(requireSigner(options.signer)), [authority]);
|
|
662
|
+
}
|
|
663
|
+
const plan = planOf(prepared, { confirmIndex: 0 });
|
|
664
|
+
if (!plan) throw new TrussetError("The role build returned no transaction to sign", "NOTHING_TO_SIGN", 0);
|
|
665
|
+
const executed = await execute(this.ctx, plan, options);
|
|
666
|
+
return confirmExecuted(executed, confirm);
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
// src/modules/lending/rates.ts
|
|
671
|
+
var RATE_MODES2 = ["CURVE", "CLAMPED", "FIXED"];
|
|
672
|
+
var EXTENSION_KINDS = ["LOAN", "BATCH", "FLOOR"];
|
|
673
|
+
var ROLE_READS = {
|
|
674
|
+
ISSUER_ROLE: ["MARKET_ISSUER"],
|
|
675
|
+
DEFAULT_ADMIN_ROLE: ["MARKET_ADMIN"],
|
|
676
|
+
ISSUER_OR_ADMIN: ["MARKET_ISSUER", "MARKET_ADMIN"]
|
|
677
|
+
};
|
|
678
|
+
async function requireSignerIs(options, required, role) {
|
|
679
|
+
if (!required) return;
|
|
680
|
+
const address = await signerAddress(requireSigner(options.signer));
|
|
681
|
+
if (!sameAddress(address, required)) {
|
|
682
|
+
throw new ValidationError(`This transaction must be sent by ${required} (${role}), but the signer is ${address}. Nothing was signed: hand the prepared transaction to that wallet, or sign with it`);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
function checkRateOffering(input) {
|
|
686
|
+
const rateMode = requireOneOf(input.rateMode, "rateMode", RATE_MODES2);
|
|
687
|
+
const rateFloorBps = optionalInt(input.rateFloorBps, "rateFloorBps", 0, 5e4);
|
|
688
|
+
const rateCapBps = optionalInt(input.rateCapBps, "rateCapBps", 1, 5e4);
|
|
689
|
+
const fixedRateBps = optionalInt(input.fixedRateBps, "fixedRateBps", 1, 5e4);
|
|
690
|
+
if (rateMode === "FIXED") {
|
|
691
|
+
if (fixedRateBps === void 0) throw new ValidationError("A FIXED rate mode requires fixedRateBps");
|
|
692
|
+
if (rateFloorBps !== void 0 || rateCapBps !== void 0) throw new ValidationError("A FIXED rate mode takes fixedRateBps only; rateFloorBps and rateCapBps belong to CLAMPED");
|
|
693
|
+
}
|
|
694
|
+
if (rateMode === "CLAMPED") {
|
|
695
|
+
if (rateCapBps === void 0) throw new ValidationError("A CLAMPED rate mode requires rateCapBps");
|
|
696
|
+
if (fixedRateBps !== void 0) throw new ValidationError("A CLAMPED rate mode takes rateFloorBps and rateCapBps only; fixedRateBps belongs to FIXED");
|
|
697
|
+
if (rateFloorBps !== void 0 && rateFloorBps > rateCapBps) throw new ValidationError("A CLAMPED cap must be at least the floor");
|
|
698
|
+
}
|
|
699
|
+
if (rateMode === "CURVE" && (rateFloorBps !== void 0 || rateCapBps !== void 0 || fixedRateBps !== void 0)) {
|
|
700
|
+
throw new ValidationError("Rate bounds need a rateMode of CLAMPED or FIXED; the CURVE default takes none");
|
|
701
|
+
}
|
|
702
|
+
return compact({ rateMode, rateFloorBps, rateCapBps, fixedRateBps });
|
|
703
|
+
}
|
|
704
|
+
function checkTermConfig(input) {
|
|
705
|
+
const maxLoanDurationDays = requireInt(input.maxLoanDurationDays, "maxLoanDurationDays", 0, 3650);
|
|
706
|
+
const termGracePeriodDays = optionalInt(input.termGracePeriodDays, "termGracePeriodDays", 1, 30);
|
|
707
|
+
const termPenaltyBps = optionalInt(input.termPenaltyBps, "termPenaltyBps", 0, 5e4);
|
|
708
|
+
let terminalDueDate;
|
|
709
|
+
if (input.terminalDueDate !== void 0 && input.terminalDueDate !== null) {
|
|
710
|
+
terminalDueDate = typeof input.terminalDueDate === "number" ? requireInt(input.terminalDueDate, "terminalDueDate", 1, Number.MAX_SAFE_INTEGER) : requireString(input.terminalDueDate, "terminalDueDate");
|
|
711
|
+
}
|
|
712
|
+
return compact({ maxLoanDurationDays, termGracePeriodDays, terminalDueDate, termPenaltyBps });
|
|
713
|
+
}
|
|
714
|
+
function checkNewDueAt(value) {
|
|
715
|
+
if (typeof value === "number") return requireInt(value, "newDueAt", 1, Number.MAX_SAFE_INTEGER);
|
|
716
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim()) && Number(value.trim()) > 0) return value.trim();
|
|
717
|
+
throw new ValidationError("newDueAt must be a unix timestamp in seconds");
|
|
718
|
+
}
|
|
719
|
+
function checkProposalId(value) {
|
|
720
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return String(value);
|
|
721
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) return value.trim();
|
|
722
|
+
throw new ValidationError("proposalId must be the whole-number proposal id from extensions()");
|
|
723
|
+
}
|
|
724
|
+
var LendingRates = class extends LendingArea {
|
|
725
|
+
path(marketId, rest) {
|
|
726
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
727
|
+
}
|
|
728
|
+
async rateMode(marketId) {
|
|
729
|
+
return this.http.get(this.path(marketId, "rate-mode"));
|
|
730
|
+
}
|
|
731
|
+
async termConfig(marketId) {
|
|
732
|
+
return this.http.get(this.path(marketId, "term-config"));
|
|
733
|
+
}
|
|
734
|
+
async exitLiquidity(marketId) {
|
|
735
|
+
return this.http.get(this.path(marketId, "exit-liquidity"));
|
|
736
|
+
}
|
|
737
|
+
async extensions(marketId) {
|
|
738
|
+
return this.http.get(this.path(marketId, "extensions"));
|
|
739
|
+
}
|
|
740
|
+
async setRateMode(marketId, input, options) {
|
|
741
|
+
return this.offering(marketId, "rate-mode", options, () => checkRateOffering(input), "the interest model owner");
|
|
742
|
+
}
|
|
743
|
+
async setMaxFixedRate(marketId, maxFixedRateBps, options) {
|
|
744
|
+
return this.offering(marketId, "max-fixed-rate", options, () => ({ maxFixedRateBps: requireInt(maxFixedRateBps, "maxFixedRateBps", 1, 5e4) }), "the interest model owner");
|
|
745
|
+
}
|
|
746
|
+
async setTermConfig(marketId, input, options) {
|
|
747
|
+
return this.offering(marketId, "term-config", options, () => checkTermConfig(input), "the market issuer");
|
|
748
|
+
}
|
|
749
|
+
async setAllowExtension(marketId, allowed, options) {
|
|
750
|
+
return this.offering(marketId, "allow-extension", options, () => ({ allowed: requireBoolean(allowed, "allowed") }), "the market admin");
|
|
751
|
+
}
|
|
752
|
+
async setReserveRatio(marketId, reserveRatioBps, options) {
|
|
753
|
+
return this.offering(marketId, "reserve-ratio", options, () => ({ reserveRatioBps: requireInt(reserveRatioBps, "reserveRatioBps", 0, 5e3) }), "the market issuer");
|
|
754
|
+
}
|
|
755
|
+
async proposeExtension(marketId, input, options) {
|
|
756
|
+
return this.extension(marketId, "extensions", options, () => {
|
|
757
|
+
const full = input;
|
|
758
|
+
const kind = requireOneOf(full.kind, "kind", EXTENSION_KINDS);
|
|
759
|
+
const loanId = full.loanId === void 0 || full.loanId === null ? void 0 : requirePositiveId(full.loanId, "loanId");
|
|
760
|
+
if (kind === "LOAN" && loanId === void 0) throw new ValidationError("A LOAN extension needs loanId");
|
|
761
|
+
if (kind !== "LOAN" && loanId !== void 0) throw new ValidationError(`A ${kind} extension takes no loanId`);
|
|
762
|
+
return compact({ kind, loanId, newDueAt: checkNewDueAt(full.newDueAt) });
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
async confirmExtension(marketId, proposalId, options) {
|
|
766
|
+
return this.extensionAction(marketId, proposalId, "confirm", void 0, options);
|
|
767
|
+
}
|
|
768
|
+
async applyExtension(marketId, proposalId, loanIds, options) {
|
|
769
|
+
return this.extensionAction(marketId, proposalId, "apply", loanIds ?? void 0, options);
|
|
770
|
+
}
|
|
771
|
+
async cancelExtension(marketId, proposalId, options) {
|
|
772
|
+
return this.extensionAction(marketId, proposalId, "cancel", void 0, options);
|
|
773
|
+
}
|
|
774
|
+
async checkSigner(marketId, options, prepared, label2) {
|
|
775
|
+
if (prepared.requiredSigner) return requireSignerIs(options, prepared.requiredSigner, label2);
|
|
776
|
+
const roles = prepared.requiredRole ? ROLE_READS[prepared.requiredRole] : void 0;
|
|
777
|
+
if (roles) return this.assertSignerHoldsAny(marketId, await signerAddress(requireSigner(options.signer)), roles);
|
|
778
|
+
if (prepared.requiredRole === "INTEREST_MODEL_OWNER") {
|
|
779
|
+
throw new TrussetError("The interest model owner could not be read just now, so nothing was signed. Retry shortly", "ROLE_UNREADABLE", 503);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
async offering(marketId, route, options, body, role) {
|
|
783
|
+
const path = this.path(marketId, route);
|
|
784
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
785
|
+
const built = options !== void 0 && !isSignOptions(options) ? null : body();
|
|
786
|
+
return this.twoLeg(options, {
|
|
787
|
+
build: async () => {
|
|
788
|
+
const prepared = await this.http.post(path, built, BUILD);
|
|
789
|
+
if (isSignOptions(options)) await this.checkSigner(marketId, options, prepared, role);
|
|
790
|
+
return prepared;
|
|
791
|
+
},
|
|
792
|
+
confirm
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
async extension(marketId, route, options, body) {
|
|
796
|
+
const path = this.path(marketId, route);
|
|
797
|
+
const confirmPath = this.path(marketId, "extensions/confirm-tx");
|
|
798
|
+
const built = options !== void 0 && !isSignOptions(options) ? null : body();
|
|
799
|
+
return this.twoLeg(options, {
|
|
800
|
+
build: async () => {
|
|
801
|
+
const prepared = await this.http.post(path, built, BUILD);
|
|
802
|
+
if (isSignOptions(options)) await this.checkSigner(marketId, options, prepared, "the role this step needs");
|
|
803
|
+
return prepared;
|
|
804
|
+
},
|
|
805
|
+
confirm: (txHash) => this.http.post(confirmPath, { txHash }, CONFIRM)
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
async extensionAction(marketId, proposalId, action, loanIds, options) {
|
|
809
|
+
const confirming = options !== void 0 && !isSignOptions(options);
|
|
810
|
+
const route = confirming ? "extensions" : `extensions/${checkProposalId(proposalId)}/${action}`;
|
|
811
|
+
return this.extension(marketId, route, options, () => {
|
|
812
|
+
if (action !== "apply") return {};
|
|
813
|
+
if (!Array.isArray(loanIds) || loanIds.length === 0 || loanIds.length > 200) {
|
|
814
|
+
throw new ValidationError("applyExtension needs 1 to 200 loanIds per page");
|
|
815
|
+
}
|
|
816
|
+
return { loanIds: loanIds.map((id, i) => requirePositiveId(id, `loanIds[${i}]`)) };
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// src/modules/lending/asset-data.ts
|
|
822
|
+
var MODES = ["FREEZE", "CUSTODY"];
|
|
823
|
+
var PRICE_SOURCES2 = ["MARKET", "NAV", "ORACLE", "FIXED"];
|
|
824
|
+
var ZERO_HASH = `0x${"0".repeat(64)}`;
|
|
825
|
+
function checkInitialPrice(value) {
|
|
826
|
+
if (value === void 0 || value === null) return void 0;
|
|
827
|
+
if (typeof value !== "string" || !/^\d+(\.\d{1,18})?$/.test(value.trim()) || !/[1-9]/.test(value)) {
|
|
828
|
+
throw new ValidationError("initialPrice must be a positive decimal string with at most 18 decimal places");
|
|
829
|
+
}
|
|
830
|
+
return value.trim();
|
|
831
|
+
}
|
|
832
|
+
var LendingAssetData = class extends LendingArea {
|
|
833
|
+
path(marketAddress, rest) {
|
|
834
|
+
return `${LENDING_BASE}/markets/${requireWallet(marketAddress, "marketAddress")}/asset-data${rest}`;
|
|
835
|
+
}
|
|
836
|
+
async preview(input) {
|
|
837
|
+
return this.http.get(`${LENDING_BASE}/markets/asset-data/preview`, {
|
|
838
|
+
collateralTokenAddress: requireWallet(input?.collateralTokenAddress, "collateralTokenAddress"),
|
|
839
|
+
mode: optionalOneOf(input.mode, "mode", MODES),
|
|
840
|
+
priceSource: optionalOneOf(input.priceSource, "priceSource", PRICE_SOURCES2),
|
|
841
|
+
initialPrice: checkInitialPrice(input.initialPrice),
|
|
842
|
+
borrowAssetAddress: optionalWallet(input.borrowAssetAddress, "borrowAssetAddress")
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
async get(marketAddress) {
|
|
846
|
+
return this.http.get(this.path(marketAddress, ""));
|
|
847
|
+
}
|
|
848
|
+
async bundle(marketAddress, options = {}) {
|
|
849
|
+
const hash = options.hash === void 0 ? void 0 : requireHash(options.hash, "hash");
|
|
850
|
+
return this.http.get(this.path(marketAddress, "/bundle"), { hash });
|
|
851
|
+
}
|
|
852
|
+
async attest(marketAddress, options) {
|
|
853
|
+
const buildPath = this.path(marketAddress, "/attest");
|
|
854
|
+
const confirmPath = this.path(marketAddress, "/attest/confirm");
|
|
855
|
+
return this.twoLeg(options, {
|
|
856
|
+
build: async () => {
|
|
857
|
+
const prepared = await this.http.post(buildPath, {}, BUILD);
|
|
858
|
+
if (isSignOptions(options)) await requireSignerIs(options, prepared.signer, "the asset data attestor");
|
|
859
|
+
return prepared;
|
|
860
|
+
},
|
|
861
|
+
confirm: (txHash) => this.http.post(confirmPath, { txHash }, CONFIRM)
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
async acknowledge(marketAddress, contentHash, options) {
|
|
865
|
+
const buildPath = this.path(marketAddress, "/acknowledge");
|
|
866
|
+
const confirmPath = this.path(marketAddress, "/acknowledge/confirm");
|
|
867
|
+
const confirming = options !== void 0 && !isSignOptions(options);
|
|
868
|
+
let hash = null;
|
|
869
|
+
if (!confirming) {
|
|
870
|
+
hash = requireHash(contentHash, "contentHash");
|
|
871
|
+
if (hash.toLowerCase() === ZERO_HASH) throw new ValidationError("contentHash must be the hash of the asset data version you reviewed, not the zero hash");
|
|
872
|
+
}
|
|
873
|
+
return this.twoLeg(options, {
|
|
874
|
+
build: () => this.http.post(buildPath, { contentHash: hash }, BUILD),
|
|
875
|
+
confirm: (txHash) => this.http.post(confirmPath, { txHash }, CONFIRM)
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// src/modules/lending/access-lists.ts
|
|
881
|
+
var PURPOSES = ["BORROWER", "DEPOSITOR", "ANY"];
|
|
882
|
+
var LIST_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
883
|
+
var MAX_BATCH = 200;
|
|
884
|
+
var MAX_ACCOUNTS = 2e3;
|
|
885
|
+
var MAX_LIVE_WALLETS = 200;
|
|
886
|
+
function listName(value) {
|
|
887
|
+
const name = requireString(value, "name");
|
|
888
|
+
if (name.length < 3 || name.length > 64) throw new ValidationError("name must be 3 to 64 characters");
|
|
889
|
+
return name;
|
|
890
|
+
}
|
|
891
|
+
function wallets(value, field, max, min = 0) {
|
|
892
|
+
if (value === void 0 && min === 0) return [];
|
|
893
|
+
if (!Array.isArray(value)) throw new ValidationError(`${field} must be an array of wallet addresses`);
|
|
894
|
+
if (value.length < min) throw new ValidationError(`${field} must name at least ${min} wallet`);
|
|
895
|
+
if (value.length > max) throw new ValidationError(`${field} takes at most ${max} wallets per request`);
|
|
896
|
+
return value.map((entry, index) => requireWallet(entry, `${field}[${index}]`));
|
|
897
|
+
}
|
|
898
|
+
function withoutTransactions(value) {
|
|
899
|
+
const copy = { ...value };
|
|
900
|
+
delete copy.transactions;
|
|
901
|
+
return copy;
|
|
902
|
+
}
|
|
903
|
+
var LendingAccessLists = class extends LendingArea {
|
|
904
|
+
path(listId, rest = "") {
|
|
905
|
+
const base = `${LENDING_BASE}/access-lists`;
|
|
906
|
+
if (listId === void 0) return rest ? `${base}/${rest}` : base;
|
|
907
|
+
const id = requireString(listId, "listId");
|
|
908
|
+
if (!LIST_ID_RE.test(id)) throw new ValidationError("listId must be the whitelist id the API returned (letters, digits, underscores and hyphens, at most 64 characters)");
|
|
909
|
+
return rest ? `${base}/${enc(id)}/${rest}` : `${base}/${enc(id)}`;
|
|
910
|
+
}
|
|
911
|
+
async list(options = {}) {
|
|
912
|
+
return this.http.get(this.path(), { includeArchived: optionalBoolean(options.includeArchived, "includeArchived") ? "true" : void 0 });
|
|
913
|
+
}
|
|
914
|
+
async get(listId, options = {}) {
|
|
915
|
+
const live = options.wallets === void 0 ? void 0 : wallets(options.wallets, "wallets", MAX_LIVE_WALLETS);
|
|
916
|
+
return this.http.get(this.path(listId), { wallets: live === void 0 ? void 0 : live.join(",") });
|
|
917
|
+
}
|
|
918
|
+
async create(input, options) {
|
|
919
|
+
const name = listName(input?.name);
|
|
920
|
+
const purpose = optionalOneOf(input?.purpose, "purpose", PURPOSES);
|
|
921
|
+
const confirm = (txHash) => this.http.post(this.path(void 0, "confirm-create"), compact({ txHash, name, purpose }), CONFIRM);
|
|
922
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
923
|
+
const full = input;
|
|
924
|
+
const body = {
|
|
925
|
+
name,
|
|
926
|
+
purpose,
|
|
927
|
+
managers: full.managers === void 0 ? void 0 : wallets(full.managers, "managers", MAX_BATCH),
|
|
928
|
+
members: full.members === void 0 ? void 0 : wallets(full.members, "members", MAX_BATCH),
|
|
929
|
+
signerAddress: optionalWallet(full.signerAddress, "signerAddress")
|
|
930
|
+
};
|
|
931
|
+
if (isSignOptions(options)) body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
932
|
+
return this.twoLeg(options, {
|
|
933
|
+
build: () => this.http.post(this.path(), compact(body), BUILD),
|
|
934
|
+
confirm
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
async setMembers(listId, input, options) {
|
|
938
|
+
const confirmPath = this.path(listId, "members/confirm");
|
|
939
|
+
const confirm = (txHash) => this.http.post(confirmPath, { txHash }, CONFIRM);
|
|
940
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
941
|
+
const body = {
|
|
942
|
+
accounts: wallets(input?.accounts, "accounts", MAX_ACCOUNTS, 1),
|
|
943
|
+
listed: requireBoolean(input?.listed, "listed"),
|
|
944
|
+
signerAddress: optionalWallet(input?.signerAddress, "signerAddress")
|
|
945
|
+
};
|
|
946
|
+
const build = () => this.http.post(this.path(listId, "members"), compact(body), BUILD);
|
|
947
|
+
if (!isSignOptions(options)) return build();
|
|
948
|
+
body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
949
|
+
await preflightChain(this.ctx, options);
|
|
950
|
+
const prepared = await build();
|
|
951
|
+
const units = prepared.action === "SIGN_TRANSACTIONS" ? prepared.steps : [prepared];
|
|
952
|
+
const transactions = [];
|
|
953
|
+
const confirmations = [];
|
|
954
|
+
for (const unit of units) {
|
|
955
|
+
const plan = planOf(unit);
|
|
956
|
+
if (!plan) throw new ValidationError("A step of the whitelist plan carries no transaction to sign");
|
|
957
|
+
try {
|
|
958
|
+
const executed = await execute(this.ctx, plan, options);
|
|
959
|
+
const confirmed = await confirmExecuted(executed, confirm);
|
|
960
|
+
transactions.push(...executed.transactions);
|
|
961
|
+
confirmations.push(withoutTransactions(confirmed));
|
|
962
|
+
} catch (err) {
|
|
963
|
+
if (transactions.length === 0) throw err;
|
|
964
|
+
const failed = err instanceof ConfirmationError ? err.transactions : [];
|
|
965
|
+
throw new ConfirmationError(
|
|
966
|
+
`The whitelist update stopped after ${confirmations.length} of ${units.length} confirmed transactions: ${err instanceof Error ? err.message : String(err)}. Confirm any listed hash with { txHash }, or rescan the list`,
|
|
967
|
+
err instanceof ConfirmationError ? err.code : "TRANSACTION_FAILED",
|
|
968
|
+
[...transactions, ...failed],
|
|
969
|
+
err instanceof ConfirmationError ? err.txHash : null,
|
|
970
|
+
err
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return { confirmations, transactions };
|
|
975
|
+
}
|
|
976
|
+
async setManager(listId, input, options) {
|
|
977
|
+
const confirmPath = this.path(listId, "managers/confirm");
|
|
978
|
+
const confirm = (txHash) => this.http.post(confirmPath, { txHash }, CONFIRM);
|
|
979
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
980
|
+
const body = {
|
|
981
|
+
account: requireWallet(input?.account, "account"),
|
|
982
|
+
enabled: requireBoolean(input?.enabled, "enabled"),
|
|
983
|
+
signerAddress: optionalWallet(input?.signerAddress, "signerAddress")
|
|
984
|
+
};
|
|
985
|
+
if (isSignOptions(options)) body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
986
|
+
return this.twoLeg(options, {
|
|
987
|
+
build: () => this.http.post(this.path(listId, "managers"), compact(body), BUILD),
|
|
988
|
+
confirm
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
async transferOwnership(listId, newOwner, input, options) {
|
|
992
|
+
assertInputIsNotOptions(input, "transferOwnership(listId, newOwner, {}, { signer })");
|
|
993
|
+
if (isConfirmOptions(options)) return this.ownerLeg(listId, {}, options);
|
|
994
|
+
return this.ownerLeg(listId, { newOwner: requireWallet(newOwner, "newOwner"), signerAddress: optionalWallet(input?.signerAddress, "signerAddress") }, options);
|
|
995
|
+
}
|
|
996
|
+
async acceptOwnership(listId, input, options) {
|
|
997
|
+
assertInputIsNotOptions(input, "acceptOwnership(listId, {}, { signer })");
|
|
998
|
+
if (isConfirmOptions(options)) return this.ownerLeg(listId, {}, options);
|
|
999
|
+
return this.ownerLeg(listId, { accept: true, signerAddress: optionalWallet(input?.signerAddress, "signerAddress") }, options);
|
|
1000
|
+
}
|
|
1001
|
+
async rescan(listId) {
|
|
1002
|
+
return this.http.post(this.path(listId, "rescan"), void 0, BUILD);
|
|
1003
|
+
}
|
|
1004
|
+
async update(listId, input) {
|
|
1005
|
+
const body = compact({
|
|
1006
|
+
name: input?.name === void 0 ? void 0 : listName(input.name),
|
|
1007
|
+
purpose: optionalOneOf(input?.purpose, "purpose", PURPOSES),
|
|
1008
|
+
archived: optionalBoolean(input?.archived, "archived")
|
|
1009
|
+
});
|
|
1010
|
+
if (Object.keys(body).length === 0) throw new ValidationError("Send a new name, a purpose or an archived flag");
|
|
1011
|
+
return this.http.patch(this.path(listId), body, BUILD);
|
|
1012
|
+
}
|
|
1013
|
+
async ownerLeg(listId, fields, options) {
|
|
1014
|
+
const confirmPath = this.path(listId, "owner/confirm");
|
|
1015
|
+
const confirm = (txHash) => this.http.post(confirmPath, { txHash }, CONFIRM);
|
|
1016
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1017
|
+
const body = { ...fields };
|
|
1018
|
+
if (isSignOptions(options)) body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
1019
|
+
return this.twoLeg(options, {
|
|
1020
|
+
build: () => this.http.post(this.path(listId, "owner"), compact(body), BUILD),
|
|
1021
|
+
confirm
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
// src/modules/lending/terms.ts
|
|
1027
|
+
var TERMS_FORMATS = ["markdown", "html"];
|
|
1028
|
+
var EXPORT_FORMATS = ["pdf", "text", "markdown"];
|
|
1029
|
+
var MAX_CONTENT = 262144;
|
|
1030
|
+
var CONTROL_CHARS = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/;
|
|
1031
|
+
var LendingTerms = class extends LendingArea {
|
|
1032
|
+
path(marketId, rest) {
|
|
1033
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1034
|
+
}
|
|
1035
|
+
async get(marketId, options = {}) {
|
|
1036
|
+
return this.http.get(this.path(marketId, "terms"), { format: optionalOneOf(options.format, "format", TERMS_FORMATS) });
|
|
1037
|
+
}
|
|
1038
|
+
async versions(marketId, options = {}) {
|
|
1039
|
+
return this.http.get(this.path(marketId, "terms/versions"), { limit: options.limit === void 0 ? void 0 : clampInt(options.limit, 1, 100, 25) });
|
|
1040
|
+
}
|
|
1041
|
+
async version(marketId, version, options = {}) {
|
|
1042
|
+
const v = String(version).trim();
|
|
1043
|
+
if (!/^\d{1,6}$/.test(v)) throw new ValidationError("version must be a whole number of at most 6 digits");
|
|
1044
|
+
return this.http.get(this.path(marketId, `terms/versions/${v}`), { format: optionalOneOf(options.format, "format", TERMS_FORMATS) });
|
|
1045
|
+
}
|
|
1046
|
+
async save(marketId, input) {
|
|
1047
|
+
const content = input.content === void 0 ? void 0 : requireString(input.content, "content");
|
|
1048
|
+
if (content !== void 0) {
|
|
1049
|
+
const normalized = input.content.replace(/\r\n/g, "\n");
|
|
1050
|
+
if (new TextEncoder().encode(normalized).length > MAX_CONTENT) throw new ValidationError(`content must be at most ${MAX_CONTENT} bytes of UTF-8`);
|
|
1051
|
+
if (CONTROL_CHARS.test(normalized)) throw new ValidationError("content contains control characters that cannot be stored");
|
|
1052
|
+
}
|
|
1053
|
+
const templateId = input.templateId === void 0 ? void 0 : requireString(input.templateId, "templateId");
|
|
1054
|
+
if (templateId !== void 0 && templateId.length > 100) throw new ValidationError("templateId must be at most 100 characters");
|
|
1055
|
+
if (content === void 0 && templateId === void 0) throw new ValidationError("Provide content, or templateId to bind a template from the instance terms library");
|
|
1056
|
+
if (input.title !== void 0 && (typeof input.title !== "string" || input.title.length > 200)) throw new ValidationError("title must be a string of at most 200 characters");
|
|
1057
|
+
const body = compact({
|
|
1058
|
+
content: input.content,
|
|
1059
|
+
title: input.title,
|
|
1060
|
+
published: optionalBoolean(input.published, "published"),
|
|
1061
|
+
templateId
|
|
1062
|
+
});
|
|
1063
|
+
return this.http.put(this.path(marketId, "terms"), body, BUILD);
|
|
1064
|
+
}
|
|
1065
|
+
async unpublish(marketId) {
|
|
1066
|
+
return this.http.delete(this.path(marketId, "terms"), BUILD);
|
|
1067
|
+
}
|
|
1068
|
+
async export(marketId, options = {}) {
|
|
1069
|
+
return this.http.getBinary(this.path(marketId, "terms/export"), { format: optionalOneOf(options.format, "format", EXPORT_FORMATS) }, { timeoutMs: DOCUMENT_TIMEOUT_MS });
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
|
|
1073
|
+
// src/modules/lending/oracle.ts
|
|
1074
|
+
function checkPriceSignature(input, exact) {
|
|
1075
|
+
if (!input || typeof input !== "object") throw new ValidationError("Provide { price, timestamp, validUntil, signature }");
|
|
1076
|
+
if (typeof input.price !== "string" || !/^\d+$/.test(input.price)) throw new ValidationError("price must be the raw integer price in the settlement asset's smallest unit, as a string of digits");
|
|
1077
|
+
const timestamp = requireInt(input.timestamp, "timestamp", 1, Number.MAX_SAFE_INTEGER);
|
|
1078
|
+
const validUntil = requireInt(input.validUntil, "validUntil", 1, Number.MAX_SAFE_INTEGER);
|
|
1079
|
+
const pattern = exact ? /^0x[0-9a-fA-F]{130}$/ : /^0x[0-9a-fA-F]+$/;
|
|
1080
|
+
if (typeof input.signature !== "string" || !pattern.test(input.signature)) {
|
|
1081
|
+
throw new ValidationError(exact ? "signature must be the 65-byte EIP-712 signature: 0x followed by 130 hex characters" : "signature must be 0x-prefixed hex");
|
|
1082
|
+
}
|
|
1083
|
+
return { price: input.price, timestamp, validUntil, signature: input.signature };
|
|
1084
|
+
}
|
|
1085
|
+
var LendingOracle = class extends LendingArea {
|
|
1086
|
+
path(marketId, rest) {
|
|
1087
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1088
|
+
}
|
|
1089
|
+
async status(marketId) {
|
|
1090
|
+
return this.http.get(this.path(marketId, "oracle-status"));
|
|
1091
|
+
}
|
|
1092
|
+
async verifySigner(marketId) {
|
|
1093
|
+
return this.http.get(this.path(marketId, "verify-signer"));
|
|
1094
|
+
}
|
|
1095
|
+
async history(marketId, options = {}) {
|
|
1096
|
+
return this.http.get(this.path(marketId, "oracle-history"), {
|
|
1097
|
+
limit: options.limit === void 0 ? void 0 : clampInt(options.limit, 1, 100, 20),
|
|
1098
|
+
offset: optionalInt(options.offset, "offset", 0, Number.MAX_SAFE_INTEGER)
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
async verifyPrice(marketId, signedPrice) {
|
|
1102
|
+
return this.http.post(this.path(marketId, "verify-price"), checkPriceSignature(signedPrice, false), { retry: "read" });
|
|
1103
|
+
}
|
|
1104
|
+
async recordAttestation(marketId, attestation) {
|
|
1105
|
+
return this.http.post(this.path(marketId, "price-attestation"), checkPriceSignature(attestation, true), BUILD);
|
|
1106
|
+
}
|
|
1107
|
+
async sync(marketId, input, options) {
|
|
1108
|
+
const path = this.path(marketId, "sync-oracle");
|
|
1109
|
+
const price = requireAmount(input?.price, "price");
|
|
1110
|
+
const confirm = (txHash) => this.http.post(path, { price, txHash }, CONFIRM);
|
|
1111
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1112
|
+
let signerAddress2 = optionalWallet(input.signerAddress, "signerAddress");
|
|
1113
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1114
|
+
return this.twoLeg(options, {
|
|
1115
|
+
build: () => this.http.post(path, compact({ price, signerAddress: signerAddress2 }), BUILD),
|
|
1116
|
+
confirm
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
async signPrice(marketId, input, options) {
|
|
1120
|
+
if (options !== void 0 && !isSignOptions(options)) {
|
|
1121
|
+
throw new ValidationError("signPrice has no confirm leg: pass { signer } to sign and record the attestation, or no options to get the typed data");
|
|
1122
|
+
}
|
|
1123
|
+
const price = requireAmount(input?.price, "price");
|
|
1124
|
+
const validitySeconds = optionalInt(input.validitySeconds, "validitySeconds", 0, Number.MAX_SAFE_INTEGER);
|
|
1125
|
+
let signerAddress2 = optionalWallet(input.signerAddress, "signerAddress");
|
|
1126
|
+
if (options) {
|
|
1127
|
+
signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1128
|
+
await preflightChain(this.ctx, options);
|
|
1129
|
+
}
|
|
1130
|
+
const request = await this.http.post(this.path(marketId, "sign-price"), compact({ price, validitySeconds, signerAddress: signerAddress2 }), BUILD);
|
|
1131
|
+
if (!options) return request;
|
|
1132
|
+
const signature = await signTypedData(this.ctx, options, request.typedData);
|
|
1133
|
+
const message = request.typedData.message;
|
|
1134
|
+
const signedPrice = {
|
|
1135
|
+
price: String(message.price),
|
|
1136
|
+
timestamp: Number(message.timestamp),
|
|
1137
|
+
validUntil: Number(message.validUntil),
|
|
1138
|
+
signature
|
|
1139
|
+
};
|
|
1140
|
+
const recorded = await this.recordAttestation(marketId, signedPrice);
|
|
1141
|
+
return { ...recorded, signedPrice };
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
// src/modules/lending/liquidity.ts
|
|
1146
|
+
var LendingLiquidity = class extends LendingArea {
|
|
1147
|
+
positionsPath(marketId, rest) {
|
|
1148
|
+
return `${LENDING_BASE}/positions/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1149
|
+
}
|
|
1150
|
+
marketsPath(marketId, rest) {
|
|
1151
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1152
|
+
}
|
|
1153
|
+
async providers(marketId) {
|
|
1154
|
+
return this.http.get(this.positionsPath(marketId, "providers"));
|
|
1155
|
+
}
|
|
1156
|
+
async providerBalance(marketId, provider) {
|
|
1157
|
+
return this.http.get(this.positionsPath(marketId, `provider-balance/${requireWallet(provider, "provider")}`));
|
|
1158
|
+
}
|
|
1159
|
+
async add(marketId, input, options) {
|
|
1160
|
+
const path = this.positionsPath(marketId, "add-liquidity");
|
|
1161
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1162
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1163
|
+
const amount = requireAmount(input?.amount, "amount");
|
|
1164
|
+
let provider = optionalWallet(input?.provider, "provider");
|
|
1165
|
+
if (isSignOptions(options)) provider = await walletOf(options, provider, "provider");
|
|
1166
|
+
return this.twoLeg(options, {
|
|
1167
|
+
build: () => this.http.post(path, compact({ amount, provider }), BUILD),
|
|
1168
|
+
confirm
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
async remove(marketId, input, options) {
|
|
1172
|
+
const path = this.positionsPath(marketId, "remove-liquidity");
|
|
1173
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1174
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1175
|
+
const shares = requireAmount(input?.shares, "shares");
|
|
1176
|
+
let provider = optionalWallet(input?.provider, "provider");
|
|
1177
|
+
if (isSignOptions(options)) provider = await walletOf(options, provider, "provider");
|
|
1178
|
+
else if (!provider) throw new ValidationError("provider is required: it is the wallet whose shares are redeemed and that signs removeLiquidity");
|
|
1179
|
+
return this.twoLeg(options, {
|
|
1180
|
+
build: () => this.http.post(path, { shares, provider }, BUILD),
|
|
1181
|
+
confirm
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
async vaultLiquidity(marketId) {
|
|
1185
|
+
return this.http.get(this.marketsPath(marketId, "vault-liquidity"));
|
|
1186
|
+
}
|
|
1187
|
+
async setVaultLiquidity(marketId, enabled, options) {
|
|
1188
|
+
const path = this.marketsPath(marketId, "vault-liquidity");
|
|
1189
|
+
const body = { enabled: requireBoolean(enabled, "enabled") };
|
|
1190
|
+
return this.oneLeg(options, () => this.http.post(path, body, BUILD));
|
|
1191
|
+
}
|
|
1192
|
+
async registerVault(marketId, vaultAddress, options) {
|
|
1193
|
+
return this.vaultRegistration(marketId, "register", vaultAddress, options);
|
|
1194
|
+
}
|
|
1195
|
+
async deregisterVault(marketId, vaultAddress, options) {
|
|
1196
|
+
return this.vaultRegistration(marketId, "deregister", vaultAddress, options);
|
|
1197
|
+
}
|
|
1198
|
+
async connectedVaults(marketId) {
|
|
1199
|
+
return this.http.get(this.marketsPath(marketId, "connected-vaults"));
|
|
1200
|
+
}
|
|
1201
|
+
async depositToVault(marketId, vaultAddress, input, options) {
|
|
1202
|
+
const path = this.marketsPath(marketId, `vaults/${requireWallet(vaultAddress, "vaultAddress")}/deposit`);
|
|
1203
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1204
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1205
|
+
const amount = requireAmount(input?.amount, "amount");
|
|
1206
|
+
let holderAddress = optionalWallet(input?.holderAddress, "holderAddress");
|
|
1207
|
+
if (isSignOptions(options)) holderAddress = await walletOf(options, holderAddress, "holderAddress");
|
|
1208
|
+
return this.twoLeg(options, {
|
|
1209
|
+
build: () => this.http.post(path, compact({ amount, holderAddress }), BUILD),
|
|
1210
|
+
confirm
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
async redeemFromVault(marketId, vaultAddress, input, options) {
|
|
1214
|
+
const path = this.marketsPath(marketId, `vaults/${requireWallet(vaultAddress, "vaultAddress")}/redeem`);
|
|
1215
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1216
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1217
|
+
const shares = requireAmount(input?.shares, "shares");
|
|
1218
|
+
let holderAddress = optionalWallet(input?.holderAddress, "holderAddress");
|
|
1219
|
+
if (isSignOptions(options)) holderAddress = await walletOf(options, holderAddress, "holderAddress");
|
|
1220
|
+
else if (!holderAddress) throw new ValidationError("holderAddress is required: it is the wallet whose vault shares are redeemed");
|
|
1221
|
+
return this.twoLeg(options, {
|
|
1222
|
+
build: () => this.http.post(path, { shares, holderAddress }, BUILD),
|
|
1223
|
+
confirm
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
async vaultRegistration(marketId, action, vaultAddress, options) {
|
|
1227
|
+
const path = this.marketsPath(marketId, `vault-liquidity/${action}`);
|
|
1228
|
+
const body = { vaultAddress: requireWallet(vaultAddress, "vaultAddress") };
|
|
1229
|
+
return this.oneLeg(options, () => this.http.post(path, body, BUILD));
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
// src/modules/lending/loans.ts
|
|
1234
|
+
var REPAY_MODES = ["sequential", "permit", "batch"];
|
|
1235
|
+
function repayAmountFields(input) {
|
|
1236
|
+
const all = optionalBoolean(input.all, "all");
|
|
1237
|
+
const amount = optionalAmount(input.amount, "amount");
|
|
1238
|
+
if (all === true && amount !== void 0) throw new ValidationError("Send either amount or all: true, not both");
|
|
1239
|
+
if (all !== true && amount === void 0) throw new ValidationError("Provide amount, or all: true to repay the loan in full");
|
|
1240
|
+
return all === true ? { all: true } : { amount };
|
|
1241
|
+
}
|
|
1242
|
+
var LendingLoans = class extends LendingArea {
|
|
1243
|
+
path(marketId, rest) {
|
|
1244
|
+
return `${LENDING_BASE}/positions/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1245
|
+
}
|
|
1246
|
+
async list(marketId) {
|
|
1247
|
+
return this.http.get(this.path(marketId, "positions"));
|
|
1248
|
+
}
|
|
1249
|
+
async get(marketId, positionId) {
|
|
1250
|
+
return this.http.get(this.path(marketId, `positions/${enc(requireRecordId(positionId, "positionId"))}`));
|
|
1251
|
+
}
|
|
1252
|
+
async statement(marketId, positionId) {
|
|
1253
|
+
return this.http.getBinary(this.path(marketId, `positions/${enc(requireRecordId(positionId, "positionId"))}/statement`), void 0, { timeoutMs: DOCUMENT_TIMEOUT_MS });
|
|
1254
|
+
}
|
|
1255
|
+
async getByLoanId(marketId, loanId) {
|
|
1256
|
+
return this.http.get(this.path(marketId, `loan/${requirePositiveId(loanId, "loanId")}`));
|
|
1257
|
+
}
|
|
1258
|
+
async byWallet(marketId, walletAddress) {
|
|
1259
|
+
return this.http.get(this.path(marketId, `user-loans/${requireWallet(walletAddress, "walletAddress")}`));
|
|
1260
|
+
}
|
|
1261
|
+
async collateral(marketId, walletAddress) {
|
|
1262
|
+
return this.http.get(this.path(marketId, `user-collateral/${requireWallet(walletAddress, "walletAddress")}`));
|
|
1263
|
+
}
|
|
1264
|
+
async escrowedCollateral(marketId, holderAddress) {
|
|
1265
|
+
return this.http.get(this.path(marketId, `escrowed-collateral/${requireWallet(holderAddress, "holderAddress")}`));
|
|
1266
|
+
}
|
|
1267
|
+
async escrowedSurplus(marketId, holderAddress) {
|
|
1268
|
+
return this.http.get(this.path(marketId, `escrowed-surplus/${requireWallet(holderAddress, "holderAddress")}`));
|
|
1269
|
+
}
|
|
1270
|
+
async open(marketId, input, options) {
|
|
1271
|
+
const path = this.path(marketId, "open-loan");
|
|
1272
|
+
const clientReference = input.clientReference === void 0 ? void 0 : requireString(input.clientReference, "clientReference");
|
|
1273
|
+
if (clientReference !== void 0 && clientReference.length > 120) throw new ValidationError("clientReference must be at most 120 characters");
|
|
1274
|
+
const reattribute = optionalBoolean(input.reattribute, "reattribute");
|
|
1275
|
+
const confirm = (txHash) => this.http.post(path, compact({ txHash, clientReference, reattribute }), CONFIRM);
|
|
1276
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1277
|
+
const full = input;
|
|
1278
|
+
const body = {
|
|
1279
|
+
collateralAmount: requireAmount(full.collateralAmount, "collateralAmount"),
|
|
1280
|
+
borrowAmount: requireAmount(full.borrowAmount, "borrowAmount"),
|
|
1281
|
+
borrowerAddress: optionalWallet(full.borrowerAddress, "borrowerAddress"),
|
|
1282
|
+
signedPrice: checkSignedPrice(full.signedPrice),
|
|
1283
|
+
clientReference,
|
|
1284
|
+
payToAddress: full.payToAddress === null ? null : optionalWallet(full.payToAddress, "payToAddress")
|
|
1285
|
+
};
|
|
1286
|
+
if (isSignOptions(options)) body.borrowerAddress = await walletOf(options, body.borrowerAddress, "borrowerAddress");
|
|
1287
|
+
else if (!body.borrowerAddress) throw new ValidationError("borrowerAddress is required: the borrower is the wallet that signs openLoan");
|
|
1288
|
+
return this.twoLeg(options, {
|
|
1289
|
+
build: () => this.http.post(path, compact(body), BUILD),
|
|
1290
|
+
confirm
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
async addCollateral(marketId, input, options) {
|
|
1294
|
+
return this.loanAction(marketId, "add-collateral", input, options, (full) => ({ amount: requireAmount(full.amount, "amount") }));
|
|
1295
|
+
}
|
|
1296
|
+
async withdrawCollateral(marketId, input, options) {
|
|
1297
|
+
return this.loanAction(marketId, "withdraw-collateral", input, options, (full) => ({ amount: requireAmount(full.amount, "amount"), signedPrice: checkSignedPrice(full.signedPrice) }));
|
|
1298
|
+
}
|
|
1299
|
+
async borrowMore(marketId, input, options) {
|
|
1300
|
+
return this.loanAction(marketId, "borrow-more", input, options, (full) => ({ amount: requireAmount(full.amount, "amount"), signedPrice: checkSignedPrice(full.signedPrice) }));
|
|
1301
|
+
}
|
|
1302
|
+
async close(marketId, loanId, options) {
|
|
1303
|
+
return this.loanAction(marketId, "close-loan", { loanId }, options, () => ({}));
|
|
1304
|
+
}
|
|
1305
|
+
async permitQuote(marketId, input) {
|
|
1306
|
+
const body = compact({
|
|
1307
|
+
loanId: requirePositiveId(input.loanId, "loanId"),
|
|
1308
|
+
...repayAmountFields(input),
|
|
1309
|
+
payer: requireWallet(input.payer, "payer"),
|
|
1310
|
+
deadlineSeconds: optionalInt(input.deadlineSeconds, "deadlineSeconds", 300, 1800)
|
|
1311
|
+
});
|
|
1312
|
+
return this.http.post(this.path(marketId, "permit-quote"), body, BUILD);
|
|
1313
|
+
}
|
|
1314
|
+
async repay(marketId, input, options) {
|
|
1315
|
+
const path = this.path(marketId, "repay");
|
|
1316
|
+
const loanId = requirePositiveId(input.loanId, "loanId");
|
|
1317
|
+
if (isConfirmOptions(options)) {
|
|
1318
|
+
const hint = input.all === true ? { all: true } : input.amount !== void 0 ? { amount: requireAmount(input.amount, "amount") } : {};
|
|
1319
|
+
return this.http.post(path, { loanId, txHash: requireTxHash(options.txHash), ...hint }, CONFIRM);
|
|
1320
|
+
}
|
|
1321
|
+
const full = input;
|
|
1322
|
+
const amountFields = repayAmountFields(full);
|
|
1323
|
+
const mode = optionalOneOf(full.mode, "mode", REPAY_MODES) ?? (full.permit ? "permit" : void 0);
|
|
1324
|
+
let payer = optionalWallet(full.payer, "payer");
|
|
1325
|
+
const signedPrice = mode === "permit" ? void 0 : checkSignedPrice(full.signedPrice);
|
|
1326
|
+
const confirm = (txHash) => this.http.post(path, { loanId, txHash, ...amountFields }, CONFIRM);
|
|
1327
|
+
const build = (body) => this.http.post(path, compact(body), BUILD);
|
|
1328
|
+
if (!isSignOptions(options)) {
|
|
1329
|
+
if (mode === "permit") {
|
|
1330
|
+
if (!full.permit) throw new ValidationError("A permit repayment needs permit { value, deadline, v, r, s }: get it from permitQuote and sign its typedData, or pass { signer } and the SDK does both");
|
|
1331
|
+
if (!payer) throw new ValidationError("A permit repayment needs payer, the wallet that signed the permit");
|
|
1332
|
+
}
|
|
1333
|
+
return build({ loanId, ...amountFields, payer, mode, signedPrice, permit: full.permit });
|
|
1334
|
+
}
|
|
1335
|
+
if (mode === "batch") {
|
|
1336
|
+
throw new ValidationError("mode batch is for a wallet that sends all steps as one atomic bundle (wallet_sendCalls). Prepare it without a signer and confirm with { txHash } of the bundle, or use mode sequential or permit with a signer");
|
|
1337
|
+
}
|
|
1338
|
+
payer = await walletOf(options, payer, "payer");
|
|
1339
|
+
await preflightChain(this.ctx, options);
|
|
1340
|
+
let permit = full.permit;
|
|
1341
|
+
let quoted = null;
|
|
1342
|
+
if (mode === "permit" && !permit) {
|
|
1343
|
+
const quote = await this.permitQuote(marketId, { loanId, ...amountFields, payer, deadlineSeconds: full.deadlineSeconds });
|
|
1344
|
+
const signature = await signTypedData(this.ctx, options, quote.typedData);
|
|
1345
|
+
const { v, r, s } = splitSignature(signature);
|
|
1346
|
+
permit = { value: quote.permit.value, deadline: quote.permit.deadline, v, r, s };
|
|
1347
|
+
quoted = { mode: "permit", payer: quote.repay.payer, ...quote.repay.all ? { all: true } : { amount: quote.repay.amount } };
|
|
1348
|
+
}
|
|
1349
|
+
const prepared = await build({ loanId, ...amountFields, payer, mode, signedPrice, permit, ...quoted ?? {} });
|
|
1350
|
+
const plan = planOf(prepared);
|
|
1351
|
+
if (!plan) throw new ValidationError("The repayment build returned no transaction to sign");
|
|
1352
|
+
const executed = await execute(this.ctx, plan, options);
|
|
1353
|
+
return confirmExecuted(executed, confirm);
|
|
1354
|
+
}
|
|
1355
|
+
async claimEscrowedCollateral(marketId, input = {}, options) {
|
|
1356
|
+
assertInputIsNotOptions(input, "claimEscrowedCollateral(marketId, {}, { signer })");
|
|
1357
|
+
const path = this.path(marketId, "claim-escrowed-collateral");
|
|
1358
|
+
let holderAddress = optionalWallet(input?.holderAddress, "holderAddress");
|
|
1359
|
+
if (isSignOptions(options)) holderAddress = await walletOf(options, holderAddress, "holderAddress");
|
|
1360
|
+
return this.twoLeg(options, {
|
|
1361
|
+
build: () => this.http.post(path, compact({ holderAddress }), BUILD),
|
|
1362
|
+
confirm: (txHash) => this.http.post(path, compact({ holderAddress, txHash }), CONFIRM)
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
async claimSurplus(marketId, input, options) {
|
|
1366
|
+
const path = this.path(marketId, "claim-surplus");
|
|
1367
|
+
let holderAddress = optionalWallet(input?.holderAddress, "holderAddress");
|
|
1368
|
+
if (isSignOptions(options)) holderAddress = await walletOf(options, holderAddress, "holderAddress");
|
|
1369
|
+
else if (!isConfirmOptions(options) && !holderAddress) throw new ValidationError("holderAddress is required: the surplus is claimed by the borrower whose loan was realized");
|
|
1370
|
+
return this.twoLeg(options, {
|
|
1371
|
+
build: () => this.http.post(path, { holderAddress }, BUILD),
|
|
1372
|
+
confirm: (txHash) => this.http.post(path, { txHash }, CONFIRM)
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
async loanAction(marketId, route, input, options, fields) {
|
|
1376
|
+
const path = this.path(marketId, route);
|
|
1377
|
+
const loanId = requirePositiveId(input.loanId, "loanId");
|
|
1378
|
+
const confirm = (txHash) => this.http.post(path, { loanId, txHash }, CONFIRM);
|
|
1379
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1380
|
+
const body = compact({ loanId, ...fields(input) });
|
|
1381
|
+
return this.twoLeg(options, {
|
|
1382
|
+
build: () => this.http.post(path, body, BUILD),
|
|
1383
|
+
confirm
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
// src/modules/lending/fees.ts
|
|
1389
|
+
var LendingFees = class extends LendingArea {
|
|
1390
|
+
path(marketId, rest) {
|
|
1391
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1392
|
+
}
|
|
1393
|
+
async split(marketId) {
|
|
1394
|
+
return this.http.get(this.path(marketId, "fee-split"));
|
|
1395
|
+
}
|
|
1396
|
+
async cashflows(marketId) {
|
|
1397
|
+
return this.http.get(this.path(marketId, "cashflows"));
|
|
1398
|
+
}
|
|
1399
|
+
async sweepOperatorFees(marketId, options) {
|
|
1400
|
+
return this.sweep(this.path(marketId, "operator-fees/sweep"), {}, options);
|
|
1401
|
+
}
|
|
1402
|
+
async sweepProtocolFees(marketId, options) {
|
|
1403
|
+
return this.sweep(this.path(marketId, "protocol-fees/sweep"), {}, options);
|
|
1404
|
+
}
|
|
1405
|
+
async sweepInfrastructureFees(marketId, options) {
|
|
1406
|
+
return this.sweep(this.path(marketId, "infrastructure-fees/sweep"), {}, options);
|
|
1407
|
+
}
|
|
1408
|
+
async sweepDistributorFees(marketId, input = {}, options) {
|
|
1409
|
+
assertInputIsNotOptions(input, "sweepDistributorFees(marketId, {}, { signer })");
|
|
1410
|
+
const provider = optionalWallet(input?.provider, "provider");
|
|
1411
|
+
return this.sweep(this.path(marketId, "distributor-fees/sweep"), compact({ provider }), options);
|
|
1412
|
+
}
|
|
1413
|
+
async rotateOperatorWallet(marketId, input, options) {
|
|
1414
|
+
const path = this.path(marketId, "operator-wallet");
|
|
1415
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1416
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1417
|
+
const full = input;
|
|
1418
|
+
const newRecipient = requireWallet(full?.newRecipient, "newRecipient");
|
|
1419
|
+
let signerAddress2 = optionalWallet(full.signerAddress, "signerAddress");
|
|
1420
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1421
|
+
return this.twoLeg(options, {
|
|
1422
|
+
build: async () => {
|
|
1423
|
+
const prepared = await this.http.post(path, compact({ newRecipient, signerAddress: signerAddress2 }), BUILD);
|
|
1424
|
+
if (isSignOptions(options)) await requireSignerIs(options, prepared.requiredSigner, "the current operator wallet");
|
|
1425
|
+
return prepared;
|
|
1426
|
+
},
|
|
1427
|
+
confirm
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
async sweep(path, body, options) {
|
|
1431
|
+
return this.twoLeg(options, {
|
|
1432
|
+
build: () => this.http.post(path, body, BUILD),
|
|
1433
|
+
confirm: (txHash) => this.http.post(path, { txHash }, CONFIRM)
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
// src/modules/lending/insurance.ts
|
|
1439
|
+
var WITHDRAWAL_ACTIONS = ["propose", "cancel", "execute"];
|
|
1440
|
+
var LendingInsurance = class extends LendingArea {
|
|
1441
|
+
path(marketId, rest) {
|
|
1442
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1443
|
+
}
|
|
1444
|
+
async status(marketId) {
|
|
1445
|
+
return this.http.get(this.path(marketId, "insurance/status"));
|
|
1446
|
+
}
|
|
1447
|
+
async topUp(marketId, input, options) {
|
|
1448
|
+
const amount = requireAmount(input?.amount, "amount");
|
|
1449
|
+
let fromAddress = optionalWallet(input.fromAddress, "fromAddress");
|
|
1450
|
+
if (isSignOptions(options)) fromAddress = await walletOf(options, fromAddress, "fromAddress");
|
|
1451
|
+
if (!fromAddress) throw new ValidationError("fromAddress is required: it is the wallet that funds the top-up");
|
|
1452
|
+
const from = fromAddress;
|
|
1453
|
+
const confirm = (txHash) => this.http.post(this.path(marketId, "insurance/record-topup"), { amount, fromAddress: from, txHash }, CONFIRM);
|
|
1454
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1455
|
+
return this.twoLeg(options, {
|
|
1456
|
+
build: () => this.http.post(this.path(marketId, "insurance/top-up"), { amount, fromAddress: from }, BUILD),
|
|
1457
|
+
confirm
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
async withdraw(marketId, input, options) {
|
|
1461
|
+
const path = this.path(marketId, "insurance/withdraw-calldata");
|
|
1462
|
+
const action = optionalOneOf(input?.action, "action", WITHDRAWAL_ACTIONS) ?? "execute";
|
|
1463
|
+
if (isConfirmOptions(options)) {
|
|
1464
|
+
return this.http.post(path, { action, txHash: requireTxHash(options.txHash) }, CONFIRM);
|
|
1465
|
+
}
|
|
1466
|
+
const body = action === "cancel" ? { action } : { action, amount: requireAmount(input?.amount, "amount"), recipient: requireWallet(input?.recipient, "recipient") };
|
|
1467
|
+
const build = async () => {
|
|
1468
|
+
const prepared2 = await this.http.post(path, body, BUILD);
|
|
1469
|
+
if (isSignOptions(options)) {
|
|
1470
|
+
if (!prepared2.fundOwner || /^0x0{40}$/i.test(prepared2.fundOwner)) {
|
|
1471
|
+
throw new TrussetError("The insurance fund owner could not be read just now, so the withdrawal was not signed. Retry shortly", "FUND_OWNER_UNREADABLE", 503);
|
|
1472
|
+
}
|
|
1473
|
+
await requireSignerIs(options, prepared2.fundOwner, "the insurance fund owner");
|
|
1474
|
+
}
|
|
1475
|
+
return prepared2;
|
|
1476
|
+
};
|
|
1477
|
+
if (!isSignOptions(options)) return this.oneLeg(options, build);
|
|
1478
|
+
await preflightChain(this.ctx, options);
|
|
1479
|
+
const prepared = await build();
|
|
1480
|
+
const plan = planOf(prepared.calldata, { confirmIndex: 0 });
|
|
1481
|
+
if (!plan) throw new TrussetError("The withdrawal build returned no transaction to sign", "NOTHING_TO_SIGN", 0);
|
|
1482
|
+
const executed = await execute(this.ctx, plan, options);
|
|
1483
|
+
if (prepared.notice?.supported !== true) return { transactions: executed.transactions };
|
|
1484
|
+
return confirmExecuted(executed, (txHash) => this.http.post(path, { action: prepared.action ?? action, txHash }, CONFIRM));
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
1487
|
+
|
|
1488
|
+
// src/modules/lending/liquidations.ts
|
|
1489
|
+
function liquidationIdOf(value) {
|
|
1490
|
+
if (typeof value === "number") return requirePositiveId(value, "liquidationId");
|
|
1491
|
+
return requireRecordId(value, "liquidationId");
|
|
1492
|
+
}
|
|
1493
|
+
var LendingLiquidations = class extends LendingArea {
|
|
1494
|
+
marketPath(marketId, rest) {
|
|
1495
|
+
return `${LENDING_BASE}/liquidations/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1496
|
+
}
|
|
1497
|
+
recordPath(prefix, liquidationId, suffix = "") {
|
|
1498
|
+
return `${LENDING_BASE}/liquidations/${prefix}/${enc(liquidationIdOf(liquidationId))}${suffix}`;
|
|
1499
|
+
}
|
|
1500
|
+
async config(marketId, query = {}) {
|
|
1501
|
+
return this.http.get(this.marketPath(marketId, "config"), { signerAddress: optionalWallet(query.signerAddress, "signerAddress") });
|
|
1502
|
+
}
|
|
1503
|
+
async liquidatable(marketId) {
|
|
1504
|
+
return this.http.get(this.marketPath(marketId, "liquidatable-loans"));
|
|
1505
|
+
}
|
|
1506
|
+
async pending(marketId) {
|
|
1507
|
+
return this.http.get(this.marketPath(marketId, "pending-liquidations"));
|
|
1508
|
+
}
|
|
1509
|
+
async settled(marketId, options = {}) {
|
|
1510
|
+
return this.http.get(this.marketPath(marketId, "settled-liquidations"), {
|
|
1511
|
+
limit: clampInt(options.limit, 1, 200, 50),
|
|
1512
|
+
offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0)
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
async stats(marketId) {
|
|
1516
|
+
return this.http.get(this.marketPath(marketId, "liquidation-stats"));
|
|
1517
|
+
}
|
|
1518
|
+
async routerStats(marketId) {
|
|
1519
|
+
return this.http.get(`${LENDING_BASE}/liquidations/router-stats`, { marketId: requireMarketId(marketId) });
|
|
1520
|
+
}
|
|
1521
|
+
async atomicSale(marketId, query = {}) {
|
|
1522
|
+
return this.http.get(this.marketPath(marketId, "atomic-sale"), { signerAddress: optionalWallet(query.signerAddress, "signerAddress") });
|
|
1523
|
+
}
|
|
1524
|
+
async setAtomicMode(marketId, input, options) {
|
|
1525
|
+
const path = this.marketPath(marketId, "atomic-mode");
|
|
1526
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1527
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1528
|
+
const mode = requireInt(input?.mode, "mode", 0, 1);
|
|
1529
|
+
let signerAddress2 = optionalWallet(input?.signerAddress, "signerAddress");
|
|
1530
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1531
|
+
return this.twoLeg(options, {
|
|
1532
|
+
build: () => this.http.post(path, compact({ mode, signerAddress: signerAddress2 }), BUILD),
|
|
1533
|
+
confirm
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
async setSaleBid(marketId, input, options) {
|
|
1537
|
+
const path = this.marketPath(marketId, "sale-bid");
|
|
1538
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1539
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1540
|
+
const priceFactorBps = requireInt(input?.priceFactorBps, "priceFactorBps", 0, 2e4);
|
|
1541
|
+
if (priceFactorBps !== 0 && priceFactorBps < 9500) {
|
|
1542
|
+
throw new ValidationError("priceFactorBps must be 0 to withdraw the bid, or between 9500 and 20000");
|
|
1543
|
+
}
|
|
1544
|
+
const validUntil = optionalInt(input?.validUntil, "validUntil", 0, Number.MAX_SAFE_INTEGER);
|
|
1545
|
+
if (priceFactorBps !== 0 && validUntil === void 0) {
|
|
1546
|
+
throw new ValidationError("validUntil (unix seconds) is required when a bid is set");
|
|
1547
|
+
}
|
|
1548
|
+
const allowance = optionalAmount(input?.allowance, "allowance", { allowZero: true });
|
|
1549
|
+
if (priceFactorBps === 0 && allowance !== void 0 && /[1-9]/.test(allowance)) {
|
|
1550
|
+
throw new ValidationError('Withdrawing the bid (priceFactorBps 0) cannot raise the allowance at the same time: that plan ends on an approval the API cannot confirm. Send allowance "0" or leave it out, and set the allowance with the next bid');
|
|
1551
|
+
}
|
|
1552
|
+
let signerAddress2 = optionalWallet(input?.signerAddress, "signerAddress");
|
|
1553
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1554
|
+
return this.twoLeg(options, {
|
|
1555
|
+
build: () => this.http.post(path, compact({ priceFactorBps, validUntil, allowance, signerAddress: signerAddress2 }), BUILD),
|
|
1556
|
+
confirm
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
async liquidate(marketId, loanId, input = {}, options) {
|
|
1560
|
+
assertInputIsNotOptions(input, "liquidate(marketId, loanId, {}, { signer })");
|
|
1561
|
+
const path = this.marketPath(marketId, `liquidate/${requirePositiveId(loanId, "loanId")}`);
|
|
1562
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
1563
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1564
|
+
const signedPrice = checkSignedPrice(input?.signedPrice);
|
|
1565
|
+
let signerAddress2 = optionalWallet(input?.signerAddress, "signerAddress");
|
|
1566
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1567
|
+
return this.twoLeg(options, {
|
|
1568
|
+
build: () => this.http.post(path, compact({ signedPrice, signerAddress: signerAddress2 }), BUILD),
|
|
1569
|
+
confirm
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
async sell(liquidationId, input, options) {
|
|
1573
|
+
const path = this.recordPath("liquidation", liquidationId, "/sell");
|
|
1574
|
+
const marketId = requireMarketId(input?.marketId);
|
|
1575
|
+
const confirm = (txHash) => this.http.post(path, { marketId, txHash }, CONFIRM);
|
|
1576
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1577
|
+
let signerAddress2 = optionalWallet(input.signerAddress, "signerAddress");
|
|
1578
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1579
|
+
return this.twoLeg(options, {
|
|
1580
|
+
build: () => this.http.post(path, compact({ marketId, signerAddress: signerAddress2 }), BUILD),
|
|
1581
|
+
confirm
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
async withdrawCollateralForSale(liquidationId, input, options) {
|
|
1585
|
+
const path = this.recordPath("withdraw-collateral", liquidationId);
|
|
1586
|
+
const body = compact({
|
|
1587
|
+
marketId: requireMarketId(input?.marketId),
|
|
1588
|
+
amount: requireAmount(input?.amount, "amount"),
|
|
1589
|
+
recipientAddress: optionalWallet(input?.recipientAddress, "recipientAddress")
|
|
1590
|
+
});
|
|
1591
|
+
return this.recordWrite(path, body, options, CONFIRM_ONCE);
|
|
1592
|
+
}
|
|
1593
|
+
async reportPartialSale(liquidationId, input, options) {
|
|
1594
|
+
const path = this.recordPath("report-partial-sale", liquidationId);
|
|
1595
|
+
const body = {
|
|
1596
|
+
marketId: requireMarketId(input?.marketId),
|
|
1597
|
+
collateralSold: requireAmount(input?.collateralSold, "collateralSold"),
|
|
1598
|
+
usdcProceeds: requireAmount(input?.usdcProceeds, "usdcProceeds")
|
|
1599
|
+
};
|
|
1600
|
+
return this.recordWrite(path, body, options, CONFIRM_ONCE);
|
|
1601
|
+
}
|
|
1602
|
+
async settle(liquidationId, input, options) {
|
|
1603
|
+
const path = this.recordPath("settle", liquidationId);
|
|
1604
|
+
const body = {
|
|
1605
|
+
marketId: requireMarketId(input?.marketId),
|
|
1606
|
+
usdcProceeds: requireAmount(input?.usdcProceeds, "usdcProceeds", { allowZero: true })
|
|
1607
|
+
};
|
|
1608
|
+
return this.recordWrite(path, body, options, CONFIRM);
|
|
1609
|
+
}
|
|
1610
|
+
async close(liquidationId, input, options) {
|
|
1611
|
+
const path = this.recordPath("close", liquidationId);
|
|
1612
|
+
return this.recordWrite(path, { marketId: requireMarketId(input?.marketId) }, options, CONFIRM);
|
|
1613
|
+
}
|
|
1614
|
+
async handleTimeout(liquidationId, input, options) {
|
|
1615
|
+
const path = this.recordPath("handle-timeout", liquidationId);
|
|
1616
|
+
return this.recordWrite(path, { marketId: requireMarketId(input?.marketId) }, options, CONFIRM_ONCE);
|
|
1617
|
+
}
|
|
1618
|
+
async reopen(liquidationId, input, options) {
|
|
1619
|
+
const path = this.recordPath("reopen", liquidationId);
|
|
1620
|
+
return this.recordWrite(path, { marketId: requireMarketId(input?.marketId) }, options, CONFIRM);
|
|
1621
|
+
}
|
|
1622
|
+
async recordWrite(path, body, options, confirmRetry) {
|
|
1623
|
+
const confirm = (txHash) => this.http.post(path, { ...body, txHash }, confirmRetry);
|
|
1624
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1625
|
+
return this.twoLeg(options, {
|
|
1626
|
+
build: () => this.http.post(path, body, BUILD),
|
|
1627
|
+
confirm
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
// src/modules/lending/auctions.ts
|
|
1633
|
+
async function deployedAddress(signer, txHash) {
|
|
1634
|
+
if (!txHash) return null;
|
|
1635
|
+
const provider = signer.provider;
|
|
1636
|
+
if (!provider || typeof provider.getTransactionReceipt !== "function") return null;
|
|
1637
|
+
try {
|
|
1638
|
+
const receipt = await provider.getTransactionReceipt(txHash);
|
|
1639
|
+
return receipt?.contractAddress ?? null;
|
|
1640
|
+
} catch {
|
|
1641
|
+
return null;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
var LendingAuctions = class extends LendingArea {
|
|
1645
|
+
liquidationsPath(marketId, rest) {
|
|
1646
|
+
return `${LENDING_BASE}/liquidations/${enc(requireMarketId(marketId))}/${rest}`;
|
|
1647
|
+
}
|
|
1648
|
+
modulePath(marketId) {
|
|
1649
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/auction-module`;
|
|
1650
|
+
}
|
|
1651
|
+
async module(marketId) {
|
|
1652
|
+
return this.http.get(this.modulePath(marketId));
|
|
1653
|
+
}
|
|
1654
|
+
async wireModule(marketId, input = {}, options) {
|
|
1655
|
+
assertInputIsNotOptions(input, "wireModule(marketId, {}, { signer })");
|
|
1656
|
+
const path = this.modulePath(marketId);
|
|
1657
|
+
const body = compact({ moduleAddress: optionalWallet(input?.moduleAddress, "moduleAddress") });
|
|
1658
|
+
const build = () => this.http.post(path, body, BUILD);
|
|
1659
|
+
if (options === void 0) return build();
|
|
1660
|
+
if (isConfirmOptions(options)) {
|
|
1661
|
+
throw new ValidationError("Wiring an auction venue has no confirm leg: the venue is read back from the chain on the next read");
|
|
1662
|
+
}
|
|
1663
|
+
if (!isSignOptions(options)) {
|
|
1664
|
+
throw new ValidationError("Pass { signer } to sign, or no options to get the unsigned transactions");
|
|
1665
|
+
}
|
|
1666
|
+
await preflightChain(this.ctx, options);
|
|
1667
|
+
const prepared = await build();
|
|
1668
|
+
const wiredModule = prepared.action === "SIGN_TRANSACTIONS" ? prepared.moduleAddress : null;
|
|
1669
|
+
const plan = planOf(prepared, { confirmIndex: null });
|
|
1670
|
+
if (!plan) return { transactions: [], moduleAddress: wiredModule };
|
|
1671
|
+
if (prepared.action === "SIGN_TRANSACTIONS") {
|
|
1672
|
+
const keys = new Set(prepared.steps.map((step) => step.key));
|
|
1673
|
+
if (keys.has("authorizeVenueOnToken") || keys.has("verifyVenueAsHolder")) {
|
|
1674
|
+
throw new ValidationError("This wiring plan includes collateral token steps that only the token's agent or its register agent can sign. Prepare it without a signer and sign each step from the wallet that holds its authority");
|
|
1675
|
+
}
|
|
1676
|
+
const signer = await walletOf(options, void 0, "signer");
|
|
1677
|
+
await this.assertSignerRoles(requireMarketId(marketId), signer, [
|
|
1678
|
+
...keys.has("setAuctionModule") ? ["MARKET_ADMIN"] : [],
|
|
1679
|
+
...keys.has("authorizeModuleOnRouter") ? ["ROUTER_ADMIN"] : []
|
|
1680
|
+
]);
|
|
1681
|
+
}
|
|
1682
|
+
const executed = await execute(this.ctx, plan, options);
|
|
1683
|
+
const moduleAddress = prepared.action === "SIGN_TRANSACTION" ? await deployedAddress(options.signer, executed.transactions[0]?.txHash ?? null) : wiredModule;
|
|
1684
|
+
return { transactions: executed.transactions, moduleAddress };
|
|
1685
|
+
}
|
|
1686
|
+
async list(marketId) {
|
|
1687
|
+
return this.http.get(this.liquidationsPath(marketId, "auctions"));
|
|
1688
|
+
}
|
|
1689
|
+
async get(marketId, auctionId) {
|
|
1690
|
+
return this.http.get(this.liquidationsPath(marketId, `auctions/${requirePositiveId(auctionId, "auctionId")}`));
|
|
1691
|
+
}
|
|
1692
|
+
async forLoan(marketId, loanId) {
|
|
1693
|
+
return this.http.get(this.liquidationsPath(marketId, `loan-auction/${requirePositiveId(loanId, "loanId")}`));
|
|
1694
|
+
}
|
|
1695
|
+
async bid(marketId, input, options) {
|
|
1696
|
+
const path = this.liquidationsPath(marketId, "bid-on-auction");
|
|
1697
|
+
const auctionId = Number(requirePositiveId(input?.auctionId, "auctionId"));
|
|
1698
|
+
const collateralAmount = requireAmount(input?.collateralAmount, "collateralAmount");
|
|
1699
|
+
const confirm = (txHash) => this.http.post(path, { auctionId, collateralAmount, txHash }, CONFIRM);
|
|
1700
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1701
|
+
let bidder = optionalWallet(input.bidder, "bidder");
|
|
1702
|
+
if (isSignOptions(options)) bidder = await walletOf(options, bidder, "bidder");
|
|
1703
|
+
return this.twoLeg(options, {
|
|
1704
|
+
build: () => this.http.post(path, compact({ auctionId, collateralAmount, bidder }), BUILD),
|
|
1705
|
+
confirm
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
async settleExpired(marketId, input, options) {
|
|
1709
|
+
const path = this.liquidationsPath(marketId, "settle-expired-auction");
|
|
1710
|
+
const auctionId = Number(requirePositiveId(input?.auctionId, "auctionId"));
|
|
1711
|
+
const confirm = (txHash) => this.http.post(path, { auctionId, txHash }, CONFIRM);
|
|
1712
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1713
|
+
let signerAddress2 = optionalWallet(input.signerAddress, "signerAddress");
|
|
1714
|
+
if (isSignOptions(options) && signerAddress2 !== void 0) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1715
|
+
return this.twoLeg(options, {
|
|
1716
|
+
build: () => this.http.post(path, compact({ auctionId, signerAddress: signerAddress2 }), BUILD),
|
|
1717
|
+
confirm
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
// src/modules/lending/hooks.ts
|
|
1723
|
+
var HOOK_TYPES = ["monitorRisk", "beforeLiquidation", "afterLiquidation"];
|
|
1724
|
+
var OPERATORS = [">", ">=", "<", "<=", "==", "!="];
|
|
1725
|
+
var FREQUENCIES = ["1m", "5m", "15m", "30m", "1h", "6h", "12h", "24h"];
|
|
1726
|
+
var PARAMETERS = ["healthFactor", "collateralValue", "debtValue", "collateralRatio", "interestAccrued"];
|
|
1727
|
+
var ACTIONS = ["buy", "inform"];
|
|
1728
|
+
function boundedString(value, field, min, max) {
|
|
1729
|
+
if (value === void 0) return void 0;
|
|
1730
|
+
if (typeof value !== "string" || value.length < min || value.length > max) {
|
|
1731
|
+
throw new ValidationError(`${field} must be a string of ${min} to ${max} characters`);
|
|
1732
|
+
}
|
|
1733
|
+
return value;
|
|
1734
|
+
}
|
|
1735
|
+
function httpUrl(value, field) {
|
|
1736
|
+
const url = boundedString(value, field, 1, 2048);
|
|
1737
|
+
if (url === void 0) return void 0;
|
|
1738
|
+
let parsed;
|
|
1739
|
+
try {
|
|
1740
|
+
parsed = new URL(url);
|
|
1741
|
+
} catch {
|
|
1742
|
+
throw new ValidationError(`${field} must be a valid URL`);
|
|
1743
|
+
}
|
|
1744
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
1745
|
+
throw new ValidationError(`${field} must be a public http(s) URL`);
|
|
1746
|
+
}
|
|
1747
|
+
return url;
|
|
1748
|
+
}
|
|
1749
|
+
function finiteNumber(value, field) {
|
|
1750
|
+
if (value === void 0) return void 0;
|
|
1751
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new ValidationError(`${field} must be a number`);
|
|
1752
|
+
return value;
|
|
1753
|
+
}
|
|
1754
|
+
function hookBody(input, creating) {
|
|
1755
|
+
if (!input || typeof input !== "object") throw new ValidationError("The hook definition must be an object");
|
|
1756
|
+
const riskFields = input.riskFields === void 0 ? void 0 : (() => {
|
|
1757
|
+
if (!Array.isArray(input.riskFields) || input.riskFields.length > 20) throw new ValidationError("riskFields takes at most 20 entries");
|
|
1758
|
+
return input.riskFields.map((f, i) => compact({
|
|
1759
|
+
path: boundedString(f?.path, `riskFields[${i}].path`, 1, 256) ?? (() => {
|
|
1760
|
+
throw new ValidationError(`riskFields[${i}].path is required`);
|
|
1761
|
+
})(),
|
|
1762
|
+
operator: optionalOneOf(f?.operator, `riskFields[${i}].operator`, OPERATORS),
|
|
1763
|
+
threshold: finiteNumber(f?.threshold, `riskFields[${i}].threshold`)
|
|
1764
|
+
}));
|
|
1765
|
+
})();
|
|
1766
|
+
const parameterChecks = input.parameterChecks === void 0 ? void 0 : (() => {
|
|
1767
|
+
if (!Array.isArray(input.parameterChecks) || input.parameterChecks.length > 10) throw new ValidationError("parameterChecks takes at most 10 entries");
|
|
1768
|
+
return input.parameterChecks.map((c, i) => ({
|
|
1769
|
+
parameter: requireOneOf(c?.parameter, `parameterChecks[${i}].parameter`, PARAMETERS),
|
|
1770
|
+
operator: requireOneOf(c?.operator, `parameterChecks[${i}].operator`, OPERATORS),
|
|
1771
|
+
threshold: boundedString(c?.threshold, `parameterChecks[${i}].threshold`, 0, 64) ?? (() => {
|
|
1772
|
+
throw new ValidationError(`parameterChecks[${i}].threshold is required`);
|
|
1773
|
+
})()
|
|
1774
|
+
}));
|
|
1775
|
+
})();
|
|
1776
|
+
const triggerHookIds = input.triggerHookIds === void 0 ? void 0 : (() => {
|
|
1777
|
+
if (!Array.isArray(input.triggerHookIds) || input.triggerHookIds.length > 10) throw new ValidationError("triggerHookIds takes at most 10 hook ids");
|
|
1778
|
+
return input.triggerHookIds.map((id, i) => boundedString(id, `triggerHookIds[${i}]`, 0, 128));
|
|
1779
|
+
})();
|
|
1780
|
+
if (input.webhookPayload !== void 0 && (input.webhookPayload === null || typeof input.webhookPayload !== "object" || Array.isArray(input.webhookPayload))) {
|
|
1781
|
+
throw new ValidationError("webhookPayload must be an object of static fields");
|
|
1782
|
+
}
|
|
1783
|
+
const body = compact({
|
|
1784
|
+
hookType: creating ? requireOneOf(input.hookType, "hookType", HOOK_TYPES) : optionalOneOf(input.hookType, "hookType", HOOK_TYPES),
|
|
1785
|
+
name: boundedString(input.name, "name", 1, 128),
|
|
1786
|
+
riskUri: httpUrl(input.riskUri, "riskUri"),
|
|
1787
|
+
riskFields,
|
|
1788
|
+
riskThreshold: finiteNumber(input.riskThreshold, "riskThreshold"),
|
|
1789
|
+
checkFrequency: optionalOneOf(input.checkFrequency, "checkFrequency", FREQUENCIES),
|
|
1790
|
+
parameterChecks,
|
|
1791
|
+
triggerHookIds,
|
|
1792
|
+
actionType: optionalOneOf(input.actionType, "actionType", ACTIONS),
|
|
1793
|
+
maxBuyPrice: boundedString(input.maxBuyPrice, "maxBuyPrice", 0, 64),
|
|
1794
|
+
webhookUrl: httpUrl(input.webhookUrl, "webhookUrl"),
|
|
1795
|
+
webhookPayload: input.webhookPayload
|
|
1796
|
+
});
|
|
1797
|
+
if (creating) {
|
|
1798
|
+
if (body.hookType === "monitorRisk" && !body.riskUri) throw new ValidationError("A monitorRisk hook needs riskUri");
|
|
1799
|
+
if (body.hookType === "monitorRisk" && (!riskFields || riskFields.length === 0)) throw new ValidationError("A monitorRisk hook needs at least one riskFields entry");
|
|
1800
|
+
if (body.hookType === "afterLiquidation" && !body.actionType) throw new ValidationError("An afterLiquidation hook needs actionType");
|
|
1801
|
+
if (body.actionType === "inform" && !body.webhookUrl) throw new ValidationError("actionType inform needs webhookUrl");
|
|
1802
|
+
}
|
|
1803
|
+
return body;
|
|
1804
|
+
}
|
|
1805
|
+
var LendingHooks = class extends LendingArea {
|
|
1806
|
+
path(marketId, hookId, rest = "") {
|
|
1807
|
+
const base = `${LENDING_BASE}/hooks/${enc(requireMarketId(marketId))}`;
|
|
1808
|
+
if (hookId === void 0) return base;
|
|
1809
|
+
const hook = `${base}/${enc(requireRecordId(hookId, "hookId"))}`;
|
|
1810
|
+
return rest ? `${hook}/${rest}` : hook;
|
|
1811
|
+
}
|
|
1812
|
+
async list(marketId) {
|
|
1813
|
+
return this.http.get(this.path(marketId));
|
|
1814
|
+
}
|
|
1815
|
+
async get(marketId, hookId) {
|
|
1816
|
+
return this.http.get(this.path(marketId, hookId));
|
|
1817
|
+
}
|
|
1818
|
+
async create(marketId, input) {
|
|
1819
|
+
return this.http.post(this.path(marketId), hookBody(input, true), BUILD);
|
|
1820
|
+
}
|
|
1821
|
+
async update(marketId, hookId, input) {
|
|
1822
|
+
return this.http.put(this.path(marketId, hookId), hookBody(input, false), BUILD);
|
|
1823
|
+
}
|
|
1824
|
+
async remove(marketId, hookId) {
|
|
1825
|
+
return this.http.delete(this.path(marketId, hookId), BUILD);
|
|
1826
|
+
}
|
|
1827
|
+
async toggle(marketId, hookId, active) {
|
|
1828
|
+
return this.http.post(this.path(marketId, hookId, "toggle"), { active: requireBoolean(active, "active") }, BUILD);
|
|
1829
|
+
}
|
|
1830
|
+
async execute(marketId, hookId, input = {}) {
|
|
1831
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
1832
|
+
throw new ValidationError("Pass {} for a monitorRisk hook, { loanData } for beforeLiquidation, or { liquidationData } for afterLiquidation");
|
|
1833
|
+
}
|
|
1834
|
+
return this.http.post(this.path(marketId, hookId, "execute"), input, BUILD);
|
|
1835
|
+
}
|
|
1836
|
+
async executions(marketId, hookId, options = {}) {
|
|
1837
|
+
return this.http.get(this.path(marketId, hookId, "executions"), {
|
|
1838
|
+
limit: optionalInt(options.limit, "limit", 1, 200),
|
|
1839
|
+
offset: optionalInt(options.offset, "offset", 0, Number.MAX_SAFE_INTEGER)
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
async deliveries(marketId, hookId, options = {}) {
|
|
1843
|
+
return this.http.get(this.path(marketId, hookId, "deliveries"), {
|
|
1844
|
+
limit: optionalInt(options.limit, "limit", 1, 200),
|
|
1845
|
+
offset: optionalInt(options.offset, "offset", 0, Number.MAX_SAFE_INTEGER)
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
async rotateSecret(marketId, hookId) {
|
|
1849
|
+
return this.http.post(this.path(marketId, hookId, "rotate-secret"), void 0, BUILD);
|
|
1850
|
+
}
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1853
|
+
// src/modules/lending/bots.ts
|
|
1854
|
+
function label(value) {
|
|
1855
|
+
if (value === void 0) return void 0;
|
|
1856
|
+
if (typeof value !== "string") throw new ValidationError("label must be a string");
|
|
1857
|
+
if (value.length > 64) throw new ValidationError("label must be at most 64 characters");
|
|
1858
|
+
return value;
|
|
1859
|
+
}
|
|
1860
|
+
var LendingBots = class extends LendingArea {
|
|
1861
|
+
path(botId, rest = "") {
|
|
1862
|
+
const base = `${LENDING_BASE}/liquidation-bots`;
|
|
1863
|
+
if (botId === void 0) return rest ? `${base}/${rest}` : base;
|
|
1864
|
+
const id = `${base}/${enc(requireRecordId(botId, "botId"))}`;
|
|
1865
|
+
return rest ? `${id}/${rest}` : id;
|
|
1866
|
+
}
|
|
1867
|
+
confirmTx(botId, txHash) {
|
|
1868
|
+
return this.http.post(this.path(botId, "confirm-tx"), { txHash }, CONFIRM);
|
|
1869
|
+
}
|
|
1870
|
+
async list(options = {}) {
|
|
1871
|
+
return this.http.get(this.path(), { includeArchived: optionalBoolean(options.includeArchived, "includeArchived") ? "true" : void 0 });
|
|
1872
|
+
}
|
|
1873
|
+
async get(botId) {
|
|
1874
|
+
return this.http.get(this.path(botId));
|
|
1875
|
+
}
|
|
1876
|
+
async transactions(botId, options = {}) {
|
|
1877
|
+
return this.http.get(this.path(botId, "transactions"), { limit: optionalInt(options.limit, "limit", 1, 200) });
|
|
1878
|
+
}
|
|
1879
|
+
async deploy(input = {}, options) {
|
|
1880
|
+
assertInputIsNotOptions(input, "deploy({}, { signer })");
|
|
1881
|
+
const botLabel = label(input?.label);
|
|
1882
|
+
const confirm = (txHash) => this.http.post(this.path(void 0, "confirm-deploy"), compact({ txHash, label: botLabel }), CONFIRM_ONCE);
|
|
1883
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
1884
|
+
let signerAddress2 = optionalWallet(input?.signerAddress, "signerAddress");
|
|
1885
|
+
if (isSignOptions(options)) signerAddress2 = await walletOf(options, signerAddress2, "signerAddress");
|
|
1886
|
+
return this.twoLeg(options, {
|
|
1887
|
+
build: () => this.http.post(this.path(), compact({ signerAddress: signerAddress2 }), BUILD),
|
|
1888
|
+
confirm
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
async connect(botId, marketId, options) {
|
|
1892
|
+
return this.connection(botId, marketId, true, options);
|
|
1893
|
+
}
|
|
1894
|
+
async disconnect(botId, marketId, options) {
|
|
1895
|
+
return this.connection(botId, marketId, false, options);
|
|
1896
|
+
}
|
|
1897
|
+
async pause(botId, options) {
|
|
1898
|
+
return this.action(botId, { action: "pause" }, options);
|
|
1899
|
+
}
|
|
1900
|
+
async resume(botId, options) {
|
|
1901
|
+
return this.action(botId, { action: "resume" }, options);
|
|
1902
|
+
}
|
|
1903
|
+
async setRefundPolicy(botId, input, options) {
|
|
1904
|
+
if (isConfirmOptions(options)) return this.confirmTx(requireRecordId(botId, "botId"), requireTxHash(options.txHash));
|
|
1905
|
+
return this.action(botId, {
|
|
1906
|
+
action: "setRefundPolicy",
|
|
1907
|
+
maxRefundPerCallerPerBlockWei: requireIntegerString(input?.maxRefundPerCallerPerBlockWei, "maxRefundPerCallerPerBlockWei"),
|
|
1908
|
+
maxRefundGasPriceWei: requireIntegerString(input?.maxRefundGasPriceWei, "maxRefundGasPriceWei")
|
|
1909
|
+
}, options);
|
|
1910
|
+
}
|
|
1911
|
+
async withdraw(botId, input, options) {
|
|
1912
|
+
assertInputIsNotOptions(input, "withdraw(botId, {}, { signer })");
|
|
1913
|
+
if (isConfirmOptions(options)) return this.confirmTx(requireRecordId(botId, "botId"), requireTxHash(options.txHash));
|
|
1914
|
+
return this.action(botId, {
|
|
1915
|
+
action: "withdraw",
|
|
1916
|
+
amountWei: input?.amountWei === void 0 ? void 0 : requireIntegerString(input.amountWei, "amountWei"),
|
|
1917
|
+
to: optionalWallet(input?.to, "to")
|
|
1918
|
+
}, options);
|
|
1919
|
+
}
|
|
1920
|
+
async fund(botId, amount, options) {
|
|
1921
|
+
const id = requireRecordId(botId, "botId");
|
|
1922
|
+
if (isConfirmOptions(options)) return this.confirmTx(id, requireTxHash(options.txHash));
|
|
1923
|
+
const value = requireAmount(amount, "amount");
|
|
1924
|
+
const fraction = value.split(".")[1];
|
|
1925
|
+
if (fraction !== void 0 && fraction.length > 18) throw new ValidationError("amount is in the native coin and takes at most 18 decimal places");
|
|
1926
|
+
return this.twoLeg(options, {
|
|
1927
|
+
build: () => this.http.post(this.path(id, "fund"), { amount: value }, BUILD),
|
|
1928
|
+
confirm: (txHash) => this.confirmTx(id, txHash)
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
async update(botId, input) {
|
|
1932
|
+
const body = {};
|
|
1933
|
+
if (input?.label !== void 0) body.label = input.label === null ? null : label(input.label);
|
|
1934
|
+
if (input?.archived !== void 0) body.archived = requireBoolean(input.archived, "archived");
|
|
1935
|
+
if (Object.keys(body).length === 0) throw new ValidationError("Send a label or an archived flag");
|
|
1936
|
+
return this.http.patch(this.path(botId), body, BUILD);
|
|
1937
|
+
}
|
|
1938
|
+
async connection(botId, marketId, connect, options) {
|
|
1939
|
+
const id = requireRecordId(botId, "botId");
|
|
1940
|
+
if (isConfirmOptions(options)) return this.confirmTx(id, requireTxHash(options.txHash));
|
|
1941
|
+
const market = requireMarketId(marketId);
|
|
1942
|
+
return this.twoLeg(options, {
|
|
1943
|
+
build: () => this.http.post(this.path(id, "connect"), { marketId: market, connect }, BUILD),
|
|
1944
|
+
confirm: (txHash) => this.confirmTx(id, txHash),
|
|
1945
|
+
check: async (prepared, signer) => {
|
|
1946
|
+
const steps = "steps" in prepared && Array.isArray(prepared.steps) ? prepared.steps : [];
|
|
1947
|
+
const authorities = new Set(steps.map((step) => step.authority));
|
|
1948
|
+
if (authorities.has("BOT_OWNER")) {
|
|
1949
|
+
const bot = await this.get(id);
|
|
1950
|
+
if (!sameAddress(bot.ownerWallet, signer)) {
|
|
1951
|
+
throw new ValidationError(`The bot is owned by ${bot.ownerWallet}, not by the signer ${signer}, and a step of this plan needs the owner. Prepare without a signer and sign each step from the wallet its authority names`);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
if (authorities.has("MARKET_ADMIN")) await this.assertSignerRoles(market, signer, ["MARKET_ADMIN"]);
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
async action(botId, body, options) {
|
|
1959
|
+
const id = requireRecordId(botId, "botId");
|
|
1960
|
+
if (isConfirmOptions(options)) return this.confirmTx(id, requireTxHash(options.txHash));
|
|
1961
|
+
if (isSignOptions(options)) {
|
|
1962
|
+
const signer = await walletOf(options, void 0, "signer");
|
|
1963
|
+
const bot = await this.get(id);
|
|
1964
|
+
if (!sameAddress(bot.ownerWallet, signer)) {
|
|
1965
|
+
throw new ValidationError(`The bot is owned by ${bot.ownerWallet}, not by the signer ${signer}, and this action is the owner's own call. Prepare without a signer and sign it from ${bot.ownerWallet}`);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
return this.twoLeg(options, {
|
|
1969
|
+
build: () => this.http.post(this.path(id, "action"), compact(body), BUILD),
|
|
1970
|
+
confirm: (txHash) => this.confirmTx(id, txHash)
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
};
|
|
1974
|
+
|
|
1975
|
+
// src/modules/lending/vaults.ts
|
|
1976
|
+
var CURRENCY_RE = /^[A-Za-z0-9.]{2,12}$/;
|
|
1977
|
+
var LendingVaults = class extends LendingArea {
|
|
1978
|
+
path(vaultId, rest = "") {
|
|
1979
|
+
const base = `${LENDING_BASE}/vaults/${enc(requireRecordId(vaultId, "vaultId"))}`;
|
|
1980
|
+
return rest ? `${base}/${rest}` : base;
|
|
1981
|
+
}
|
|
1982
|
+
groupPath(groupId, rest = "") {
|
|
1983
|
+
const base = `${LENDING_BASE}/vaults/groups/${enc(requireRecordId(groupId, "groupId"))}`;
|
|
1984
|
+
return rest ? `${base}/${rest}` : base;
|
|
1985
|
+
}
|
|
1986
|
+
confirmTx(vaultId, txHash, txType) {
|
|
1987
|
+
return this.http.post(this.path(vaultId, "confirm-tx"), { txHash, txType }, CONFIRM);
|
|
1988
|
+
}
|
|
1989
|
+
async list() {
|
|
1990
|
+
return this.http.get(`${LENDING_BASE}/vaults`);
|
|
1991
|
+
}
|
|
1992
|
+
async get(vaultId, options = {}) {
|
|
1993
|
+
return this.http.get(this.path(vaultId), { address: optionalWallet(options.address, "address") });
|
|
1994
|
+
}
|
|
1995
|
+
async group(groupId, options = {}) {
|
|
1996
|
+
return this.http.get(this.groupPath(groupId), { holderAddress: optionalWallet(options.holderAddress, "holderAddress") });
|
|
1997
|
+
}
|
|
1998
|
+
async transactions(vaultId, options = {}) {
|
|
1999
|
+
return this.http.get(this.path(vaultId, "transactions"), {
|
|
2000
|
+
limit: optionalInt(options.limit, "limit", 1, 200),
|
|
2001
|
+
offset: optionalInt(options.offset, "offset", 0, Number.MAX_SAFE_INTEGER),
|
|
2002
|
+
address: optionalWallet(options.address, "address")
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
async position(vaultId, holderAddress) {
|
|
2006
|
+
return this.http.get(this.path(vaultId, `position/${requireWallet(holderAddress, "holderAddress")}`));
|
|
2007
|
+
}
|
|
2008
|
+
async earnings(vaultId) {
|
|
2009
|
+
return this.http.get(this.path(vaultId, "earnings"));
|
|
2010
|
+
}
|
|
2011
|
+
async deposit(vaultId, input, options) {
|
|
2012
|
+
const id = requireRecordId(vaultId, "vaultId");
|
|
2013
|
+
if (isConfirmOptions(options)) return this.confirmTx(id, requireTxHash(options.txHash), "DEPOSIT");
|
|
2014
|
+
const body = {
|
|
2015
|
+
amount: requireAmount(input?.amount, "amount"),
|
|
2016
|
+
holderAddress: optionalWallet(input?.holderAddress, "holderAddress")
|
|
2017
|
+
};
|
|
2018
|
+
if (isSignOptions(options)) body.holderAddress = await walletOf(options, body.holderAddress, "holderAddress");
|
|
2019
|
+
return this.twoLeg(options, {
|
|
2020
|
+
build: () => this.http.post(this.path(id, "deposit"), compact(body), BUILD),
|
|
2021
|
+
confirm: (txHash) => this.confirmTx(id, txHash, "DEPOSIT")
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
async redeem(vaultId, input, options) {
|
|
2025
|
+
const id = requireRecordId(vaultId, "vaultId");
|
|
2026
|
+
if (isConfirmOptions(options)) return this.confirmTx(id, requireTxHash(options.txHash), "REDEEM");
|
|
2027
|
+
const body = {
|
|
2028
|
+
shares: requireAmount(input?.shares, "shares"),
|
|
2029
|
+
holderAddress: optionalWallet(input?.holderAddress, "holderAddress")
|
|
2030
|
+
};
|
|
2031
|
+
if (isSignOptions(options)) body.holderAddress = await walletOf(options, body.holderAddress, "holderAddress");
|
|
2032
|
+
else if (!body.holderAddress) throw new ValidationError("holderAddress is required: it is the wallet whose shares are redeemed");
|
|
2033
|
+
return this.twoLeg(options, {
|
|
2034
|
+
build: () => this.http.post(this.path(id, "redeem"), body, BUILD),
|
|
2035
|
+
confirm: (txHash) => this.confirmTx(id, txHash, "REDEEM")
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
async depositToGroup(groupId, input, options) {
|
|
2039
|
+
const path = this.groupPath(groupId, "deposit");
|
|
2040
|
+
if (isConfirmOptions(options)) {
|
|
2041
|
+
const pocket = requireRecordId(input?.vaultId, "vaultId");
|
|
2042
|
+
return this.confirmTx(pocket, requireTxHash(options.txHash), "DEPOSIT");
|
|
2043
|
+
}
|
|
2044
|
+
const full = input;
|
|
2045
|
+
const currency = full?.currency === void 0 ? void 0 : requireString(full.currency, "currency");
|
|
2046
|
+
if (currency !== void 0 && !CURRENCY_RE.test(currency)) {
|
|
2047
|
+
throw new ValidationError("currency must be a currency or asset symbol such as USDC, EURC, USD or EUR");
|
|
2048
|
+
}
|
|
2049
|
+
const asset = optionalWallet(full?.asset, "asset");
|
|
2050
|
+
if (currency === void 0 === (asset === void 0)) {
|
|
2051
|
+
throw new ValidationError("Name the pocket by exactly one of currency and asset");
|
|
2052
|
+
}
|
|
2053
|
+
const body = {
|
|
2054
|
+
amount: requireAmount(full?.amount, "amount"),
|
|
2055
|
+
currency,
|
|
2056
|
+
asset,
|
|
2057
|
+
holderAddress: optionalWallet(full?.holderAddress, "holderAddress")
|
|
2058
|
+
};
|
|
2059
|
+
if (isSignOptions(options)) body.holderAddress = await walletOf(options, body.holderAddress, "holderAddress");
|
|
2060
|
+
return this.twoLeg(options, {
|
|
2061
|
+
build: () => this.http.post(path, compact(body), BUILD),
|
|
2062
|
+
confirm: (txHash, prepared) => {
|
|
2063
|
+
if (!prepared?.vaultId) throw new ValidationError("The group deposit build named no pocket vault to confirm against");
|
|
2064
|
+
return this.confirmTx(prepared.vaultId, txHash, "DEPOSIT");
|
|
2065
|
+
}
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
async collectDistributorFees(vaultId, input, options) {
|
|
2069
|
+
return this.distributorAction(vaultId, "distributor-fees/collect", options, async () => {
|
|
2070
|
+
const body = {
|
|
2071
|
+
marketAddress: requireWallet(input?.marketAddress, "marketAddress"),
|
|
2072
|
+
signerAddress: optionalWallet(input?.signerAddress, "signerAddress")
|
|
2073
|
+
};
|
|
2074
|
+
if (isSignOptions(options)) body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
2075
|
+
return body;
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
async payDistributor(vaultId, input, options) {
|
|
2079
|
+
assertInputIsNotOptions(input, "payDistributor(vaultId, {}, { signer })");
|
|
2080
|
+
return this.distributorAction(vaultId, "distributor-fees/pay", options, async () => ({
|
|
2081
|
+
distributor: optionalWallet(input?.distributor, "distributor")
|
|
2082
|
+
}));
|
|
2083
|
+
}
|
|
2084
|
+
async payOperatorFees(vaultId, options) {
|
|
2085
|
+
return this.distributorAction(vaultId, "operator-fees/pay", options, async () => ({}));
|
|
2086
|
+
}
|
|
2087
|
+
async setDistributorWallet(vaultId, input, options) {
|
|
2088
|
+
return this.distributorAction(vaultId, "distributor-wallet", options, async () => {
|
|
2089
|
+
const body = {
|
|
2090
|
+
wallet: requireWallet(input?.wallet, "wallet"),
|
|
2091
|
+
distributor: optionalWallet(input?.distributor, "distributor"),
|
|
2092
|
+
signerAddress: optionalWallet(input?.signerAddress, "signerAddress")
|
|
2093
|
+
};
|
|
2094
|
+
if (isSignOptions(options)) body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
2095
|
+
return body;
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
async setDistributorApproval(vaultId, input, options) {
|
|
2099
|
+
return this.distributorAction(vaultId, "distributor-approval", options, async () => {
|
|
2100
|
+
const body = {
|
|
2101
|
+
distributor: requireWallet(input?.distributor, "distributor"),
|
|
2102
|
+
approved: requireBoolean(input?.approved, "approved"),
|
|
2103
|
+
signerAddress: optionalWallet(input?.signerAddress, "signerAddress")
|
|
2104
|
+
};
|
|
2105
|
+
if (isSignOptions(options)) body.signerAddress = await walletOf(options, body.signerAddress, "signerAddress");
|
|
2106
|
+
return body;
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
async distributorAction(vaultId, action, options, body) {
|
|
2110
|
+
const path = this.path(vaultId, action);
|
|
2111
|
+
const confirm = (txHash) => this.http.post(path, { txHash }, CONFIRM);
|
|
2112
|
+
if (isConfirmOptions(options)) return confirm(requireTxHash(options.txHash));
|
|
2113
|
+
const payload = compact(await body());
|
|
2114
|
+
return this.twoLeg(options, {
|
|
2115
|
+
build: () => this.http.post(path, payload, BUILD),
|
|
2116
|
+
confirm
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
};
|
|
2120
|
+
|
|
2121
|
+
// src/modules/lending/pledges.ts
|
|
2122
|
+
var LendingPledges = class extends LendingArea {
|
|
2123
|
+
async list(marketAddress, options = {}) {
|
|
2124
|
+
const includeEnded = optionalBoolean(options.includeEnded, "includeEnded");
|
|
2125
|
+
return this.http.get(`${LENDING_BASE}/pledges/${requireWallet(marketAddress, "marketAddress")}`, {
|
|
2126
|
+
offset: optionalInt(options.offset, "offset", 0, Number.MAX_SAFE_INTEGER),
|
|
2127
|
+
limit: optionalInt(options.limit, "limit", 1, 200),
|
|
2128
|
+
includeEnded: includeEnded === void 0 ? void 0 : String(includeEnded)
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
};
|
|
2132
|
+
|
|
2133
|
+
// src/modules/lending/documents.ts
|
|
2134
|
+
var VISIBILITIES = ["public", "private"];
|
|
2135
|
+
var LOAN_ID_RE = /^\d{1,30}$/;
|
|
2136
|
+
var SHA256_RE = /^(0x)?[0-9a-fA-F]{64}$/;
|
|
2137
|
+
function loanIdOf(value) {
|
|
2138
|
+
const text = value === void 0 || value === null ? "0" : String(value).trim();
|
|
2139
|
+
if (!LOAN_ID_RE.test(text)) throw new ValidationError("loanId must be a whole number: 0 for the market itself, or the id of a loan the market opened");
|
|
2140
|
+
return text;
|
|
2141
|
+
}
|
|
2142
|
+
function contentHashOf(value) {
|
|
2143
|
+
if (typeof value !== "string" || !SHA256_RE.test(value.trim())) {
|
|
2144
|
+
throw new ValidationError("contentHash must be the SHA-256 of the exact file bytes: 64 hex characters, with or without 0x");
|
|
2145
|
+
}
|
|
2146
|
+
return value.trim();
|
|
2147
|
+
}
|
|
2148
|
+
function documentBody(input) {
|
|
2149
|
+
return compact({
|
|
2150
|
+
loanId: loanIdOf(input?.loanId),
|
|
2151
|
+
name: requireString(input?.name, "name"),
|
|
2152
|
+
contentHash: contentHashOf(input?.contentHash),
|
|
2153
|
+
visibility: requireOneOf(input?.visibility, "visibility", VISIBILITIES),
|
|
2154
|
+
uri: input?.uri === void 0 ? void 0 : requireString(input.uri, "uri")
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
async function sha256Hex(bytes) {
|
|
2158
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes)));
|
|
2159
|
+
return Array.from(digest, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
2160
|
+
}
|
|
2161
|
+
var LendingDocuments = class extends LendingArea {
|
|
2162
|
+
path(marketId, rest) {
|
|
2163
|
+
return `${LENDING_BASE}/markets/${enc(requireMarketId(marketId))}/legal-documents${rest}`;
|
|
2164
|
+
}
|
|
2165
|
+
async list(marketId, loanId) {
|
|
2166
|
+
return this.http.get(this.path(marketId, ""), { loanId: loanId === void 0 ? void 0 : loanIdOf(loanId) });
|
|
2167
|
+
}
|
|
2168
|
+
async set(marketId, input, options) {
|
|
2169
|
+
return this.write(marketId, this.path(marketId, ""), { action: "set", ...documentBody(input) }, options);
|
|
2170
|
+
}
|
|
2171
|
+
async remove(marketId, input, options) {
|
|
2172
|
+
const body = { action: "remove", loanId: loanIdOf(input?.loanId), name: requireString(input?.name, "name") };
|
|
2173
|
+
return this.write(marketId, this.path(marketId, ""), body, options);
|
|
2174
|
+
}
|
|
2175
|
+
async stage(marketId, input) {
|
|
2176
|
+
return this.http.post(this.path(marketId, "/stage"), { ...documentBody(input), submittedBy: requireWallet(input?.submittedBy, "submittedBy") }, BUILD);
|
|
2177
|
+
}
|
|
2178
|
+
async record(marketId, stageId, options) {
|
|
2179
|
+
const path = this.path(marketId, `/staged/${enc(requireRecordId(stageId, "stageId"))}/record`);
|
|
2180
|
+
return this.write(marketId, path, {}, options);
|
|
2181
|
+
}
|
|
2182
|
+
async dismiss(marketId, stageId) {
|
|
2183
|
+
return this.http.post(this.path(marketId, `/staged/${enc(requireRecordId(stageId, "stageId"))}/dismiss`), {}, BUILD);
|
|
2184
|
+
}
|
|
2185
|
+
async upload(file, input) {
|
|
2186
|
+
if (input?.noPersonalData !== true) {
|
|
2187
|
+
throw new ValidationError("A public document is pinned to IPFS for good: confirm it carries no personal data with { noPersonalData: true }");
|
|
2188
|
+
}
|
|
2189
|
+
let bytes;
|
|
2190
|
+
if (typeof Blob !== "undefined" && file instanceof Blob) bytes = new Uint8Array(await file.arrayBuffer());
|
|
2191
|
+
else if (file instanceof Uint8Array) bytes = Uint8Array.from(file);
|
|
2192
|
+
else throw new ValidationError("file must be a Blob or a Uint8Array of the PDF bytes");
|
|
2193
|
+
if (bytes.length === 0) throw new ValidationError("file is empty");
|
|
2194
|
+
const contentHash = input.contentHash === void 0 ? await sha256Hex(bytes) : contentHashOf(input.contentHash);
|
|
2195
|
+
const form = new FormData();
|
|
2196
|
+
form.append("contentHash", contentHash);
|
|
2197
|
+
form.append("noPersonalData", "true");
|
|
2198
|
+
form.append("file", new Blob([bytes], { type: "application/pdf" }), "document.pdf");
|
|
2199
|
+
return this.http.postForm(`${LENDING_BASE}/markets/legal-documents/upload`, form, void 0, { timeoutMs: DOCUMENT_TIMEOUT_MS });
|
|
2200
|
+
}
|
|
2201
|
+
async write(marketId, path, body, options) {
|
|
2202
|
+
const confirm = (txHash) => this.http.post(this.path(marketId, "/confirm"), { txHash: requireTxHash(txHash) }, CONFIRM);
|
|
2203
|
+
const signerAddress2 = isSignOptions(options) ? await walletOf(options, void 0, "signerAddress") : void 0;
|
|
2204
|
+
return this.twoLeg(options, {
|
|
2205
|
+
build: () => this.http.post(path, compact({ ...body, signerAddress: signerAddress2 }), BUILD),
|
|
2206
|
+
confirm,
|
|
2207
|
+
check: async (prepared, signer) => {
|
|
2208
|
+
if (prepared.signer && !sameAddress(prepared.signer, signer)) {
|
|
2209
|
+
throw new ValidationError(`This document record must be signed by the market admin ${prepared.signer}, but the signer is ${signer}. Nothing was signed`);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
};
|
|
2215
|
+
|
|
2216
|
+
// src/modules/lending/index.ts
|
|
2217
|
+
var Lending = class {
|
|
2218
|
+
constructor(http, context = new LendingContext(http)) {
|
|
2219
|
+
this.markets = new LendingMarkets(context);
|
|
2220
|
+
this.operators = new LendingOperators(context);
|
|
2221
|
+
this.assetData = new LendingAssetData(context);
|
|
2222
|
+
this.accessLists = new LendingAccessLists(context);
|
|
2223
|
+
this.rates = new LendingRates(context);
|
|
2224
|
+
this.terms = new LendingTerms(context);
|
|
2225
|
+
this.oracle = new LendingOracle(context);
|
|
2226
|
+
this.liquidity = new LendingLiquidity(context);
|
|
2227
|
+
this.loans = new LendingLoans(context);
|
|
2228
|
+
this.fees = new LendingFees(context);
|
|
2229
|
+
this.insurance = new LendingInsurance(context);
|
|
2230
|
+
this.liquidations = new LendingLiquidations(context);
|
|
2231
|
+
this.auctions = new LendingAuctions(context);
|
|
2232
|
+
this.hooks = new LendingHooks(context);
|
|
2233
|
+
this.bots = new LendingBots(context);
|
|
2234
|
+
this.vaults = new LendingVaults(context);
|
|
2235
|
+
this.pledges = new LendingPledges(context);
|
|
2236
|
+
this.documents = new LendingDocuments(context);
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
|
|
2240
|
+
export {
|
|
2241
|
+
LendingMarkets,
|
|
2242
|
+
LendingOperators,
|
|
2243
|
+
LendingRates,
|
|
2244
|
+
LendingAssetData,
|
|
2245
|
+
LendingAccessLists,
|
|
2246
|
+
LendingTerms,
|
|
2247
|
+
LendingOracle,
|
|
2248
|
+
LendingLiquidity,
|
|
2249
|
+
LendingLoans,
|
|
2250
|
+
LendingFees,
|
|
2251
|
+
LendingInsurance,
|
|
2252
|
+
LendingLiquidations,
|
|
2253
|
+
LendingAuctions,
|
|
2254
|
+
LendingHooks,
|
|
2255
|
+
LendingBots,
|
|
2256
|
+
LendingVaults,
|
|
2257
|
+
LendingPledges,
|
|
2258
|
+
LendingDocuments,
|
|
2259
|
+
Lending
|
|
2260
|
+
};
|