@permissionless-technologies/upd-sdk 0.1.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 +172 -0
- package/dist/chunk-4RBWWS2X.js +6616 -0
- package/dist/chunk-4RBWWS2X.js.map +1 -0
- package/dist/chunk-4VXNJTNQ.cjs +58 -0
- package/dist/chunk-4VXNJTNQ.cjs.map +1 -0
- package/dist/chunk-5NNXIJE4.js +16 -0
- package/dist/chunk-5NNXIJE4.js.map +1 -0
- package/dist/chunk-63FIKV36.js +49 -0
- package/dist/chunk-63FIKV36.js.map +1 -0
- package/dist/chunk-CZEDT3MS.cjs +62 -0
- package/dist/chunk-CZEDT3MS.cjs.map +1 -0
- package/dist/chunk-DF34ON56.cjs +18 -0
- package/dist/chunk-DF34ON56.cjs.map +1 -0
- package/dist/chunk-DJBU2OEB.js +57 -0
- package/dist/chunk-DJBU2OEB.js.map +1 -0
- package/dist/chunk-LNGWRYGY.js +3 -0
- package/dist/chunk-LNGWRYGY.js.map +1 -0
- package/dist/chunk-POBNO37G.cjs +4 -0
- package/dist/chunk-POBNO37G.cjs.map +1 -0
- package/dist/chunk-R64I3LAO.js +701 -0
- package/dist/chunk-R64I3LAO.js.map +1 -0
- package/dist/chunk-RIRT4JX6.js +1808 -0
- package/dist/chunk-RIRT4JX6.js.map +1 -0
- package/dist/chunk-WRPVPA7E.cjs +713 -0
- package/dist/chunk-WRPVPA7E.cjs.map +1 -0
- package/dist/chunk-ZDAHLZWC.cjs +1812 -0
- package/dist/chunk-ZDAHLZWC.cjs.map +1 -0
- package/dist/chunk-ZSWETUGH.cjs +6623 -0
- package/dist/chunk-ZSWETUGH.cjs.map +1 -0
- package/dist/constants-Bk2bGYfX.d.ts +64 -0
- package/dist/constants-BlOP_9dy.d.cts +64 -0
- package/dist/contracts/index.cjs +45 -0
- package/dist/contracts/index.cjs.map +1 -0
- package/dist/contracts/index.d.cts +6559 -0
- package/dist/contracts/index.d.ts +6559 -0
- package/dist/contracts/index.js +4 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/index.cjs +76 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +5 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +7 -0
- package/dist/core/index.js.map +1 -0
- package/dist/health-BUb4ItNt.d.ts +46 -0
- package/dist/health-DPxBqH7b.d.cts +46 -0
- package/dist/index-XNClksom.d.ts +415 -0
- package/dist/index-yRBqVOHV.d.cts +415 -0
- package/dist/index.cjs +138 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/oracle/index.cjs +30 -0
- package/dist/oracle/index.cjs.map +1 -0
- package/dist/oracle/index.d.cts +41 -0
- package/dist/oracle/index.d.ts +41 -0
- package/dist/oracle/index.js +5 -0
- package/dist/oracle/index.js.map +1 -0
- package/dist/react/index.cjs +383 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +339 -0
- package/dist/react/index.d.ts +339 -0
- package/dist/react/index.js +370 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-DySv82My.d.cts +70 -0
- package/dist/types-DySv82My.d.ts +70 -0
- package/package.json +93 -0
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import { computeSystemHealth } from './chunk-63FIKV36.js';
|
|
2
|
+
import { UPD_TOKEN_ABI, OVERCOLLATERALIZATION_REPORTER_ABI, STABILIZER_NFT_ABI, POSITION_ESCROW_ABI, STABILIZER_ESCROW_ABI, SUPD_ABI } from './chunk-4RBWWS2X.js';
|
|
3
|
+
import { createOracleClient } from './chunk-DJBU2OEB.js';
|
|
4
|
+
import { hexToBytes, bytesToHex } from 'viem';
|
|
5
|
+
|
|
6
|
+
// src/deployments/31337.json
|
|
7
|
+
var __default = {
|
|
8
|
+
contracts: {
|
|
9
|
+
UPDToken: "0x0000000000000000000000000000000000000000",
|
|
10
|
+
PriceOracle: "0x0000000000000000000000000000000000000000",
|
|
11
|
+
StabilizerNFT: "0x0000000000000000000000000000000000000000",
|
|
12
|
+
OvercollateralizationReporter: "0x0000000000000000000000000000000000000000",
|
|
13
|
+
InsuranceEscrow: "0x0000000000000000000000000000000000000000",
|
|
14
|
+
BridgeEscrow: "0x0000000000000000000000000000000000000000",
|
|
15
|
+
sUPD: "0x0000000000000000000000000000000000000000"
|
|
16
|
+
},
|
|
17
|
+
implementations: {
|
|
18
|
+
PriceOracle: "0x0000000000000000000000000000000000000000",
|
|
19
|
+
StabilizerNFT: "0x0000000000000000000000000000000000000000",
|
|
20
|
+
OvercollateralizationReporter: "0x0000000000000000000000000000000000000000"
|
|
21
|
+
},
|
|
22
|
+
libraries: {
|
|
23
|
+
LinkedListLib: "0x0000000000000000000000000000000000000000",
|
|
24
|
+
CollateralMathLib: "0x0000000000000000000000000000000000000000"
|
|
25
|
+
},
|
|
26
|
+
templates: {
|
|
27
|
+
StabilizerEscrow: "0x0000000000000000000000000000000000000000",
|
|
28
|
+
PositionEscrow: "0x0000000000000000000000000000000000000000"
|
|
29
|
+
},
|
|
30
|
+
external: {
|
|
31
|
+
stETH: "0x0000000000000000000000000000000000000000",
|
|
32
|
+
lido: "0x0000000000000000000000000000000000000000",
|
|
33
|
+
oracleSigner: "0x0000000000000000000000000000000000000000"
|
|
34
|
+
},
|
|
35
|
+
metadata: {
|
|
36
|
+
chainId: 31337,
|
|
37
|
+
deployBlock: 0,
|
|
38
|
+
deployTimestamp: 0,
|
|
39
|
+
deployer: "0x0000000000000000000000000000000000000000"
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/deployments/11155111.json
|
|
44
|
+
var __default2 = {
|
|
45
|
+
contracts: {
|
|
46
|
+
UPDToken: "0x0000000000000000000000000000000000000000",
|
|
47
|
+
PriceOracle: "0x0000000000000000000000000000000000000000",
|
|
48
|
+
StabilizerNFT: "0x0000000000000000000000000000000000000000",
|
|
49
|
+
OvercollateralizationReporter: "0x0000000000000000000000000000000000000000",
|
|
50
|
+
InsuranceEscrow: "0x0000000000000000000000000000000000000000",
|
|
51
|
+
BridgeEscrow: "0x0000000000000000000000000000000000000000",
|
|
52
|
+
sUPD: "0x0000000000000000000000000000000000000000"
|
|
53
|
+
},
|
|
54
|
+
implementations: {
|
|
55
|
+
PriceOracle: "0x0000000000000000000000000000000000000000",
|
|
56
|
+
StabilizerNFT: "0x0000000000000000000000000000000000000000",
|
|
57
|
+
OvercollateralizationReporter: "0x0000000000000000000000000000000000000000"
|
|
58
|
+
},
|
|
59
|
+
libraries: {
|
|
60
|
+
LinkedListLib: "0x0000000000000000000000000000000000000000",
|
|
61
|
+
CollateralMathLib: "0x0000000000000000000000000000000000000000"
|
|
62
|
+
},
|
|
63
|
+
templates: {
|
|
64
|
+
StabilizerEscrow: "0x0000000000000000000000000000000000000000",
|
|
65
|
+
PositionEscrow: "0x0000000000000000000000000000000000000000"
|
|
66
|
+
},
|
|
67
|
+
external: {
|
|
68
|
+
stETH: "0x0000000000000000000000000000000000000000",
|
|
69
|
+
lido: "0x0000000000000000000000000000000000000000",
|
|
70
|
+
oracleSigner: "0x0000000000000000000000000000000000000000"
|
|
71
|
+
},
|
|
72
|
+
metadata: {
|
|
73
|
+
chainId: 11155111,
|
|
74
|
+
deployBlock: 0,
|
|
75
|
+
deployTimestamp: 0,
|
|
76
|
+
deployer: "0x0000000000000000000000000000000000000000"
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/deployments/index.ts
|
|
81
|
+
function parseDeployment(json, chainId) {
|
|
82
|
+
return {
|
|
83
|
+
chainId,
|
|
84
|
+
contracts: {
|
|
85
|
+
UPDToken: json.contracts.UPDToken,
|
|
86
|
+
PriceOracle: json.contracts.PriceOracle,
|
|
87
|
+
StabilizerNFT: json.contracts.StabilizerNFT,
|
|
88
|
+
OvercollateralizationReporter: json.contracts.OvercollateralizationReporter,
|
|
89
|
+
InsuranceEscrow: json.contracts.InsuranceEscrow,
|
|
90
|
+
BridgeEscrow: json.contracts.BridgeEscrow,
|
|
91
|
+
sUPD: json.contracts.sUPD
|
|
92
|
+
},
|
|
93
|
+
implementations: {
|
|
94
|
+
PriceOracle: json.implementations.PriceOracle,
|
|
95
|
+
StabilizerNFT: json.implementations.StabilizerNFT,
|
|
96
|
+
OvercollateralizationReporter: json.implementations.OvercollateralizationReporter
|
|
97
|
+
},
|
|
98
|
+
libraries: {
|
|
99
|
+
LinkedListLib: json.libraries.LinkedListLib,
|
|
100
|
+
CollateralMathLib: json.libraries.CollateralMathLib
|
|
101
|
+
},
|
|
102
|
+
templates: {
|
|
103
|
+
StabilizerEscrow: json.templates.StabilizerEscrow,
|
|
104
|
+
PositionEscrow: json.templates.PositionEscrow
|
|
105
|
+
},
|
|
106
|
+
external: {
|
|
107
|
+
stETH: json.external.stETH,
|
|
108
|
+
lido: json.external.lido,
|
|
109
|
+
oracleSigner: json.external.oracleSigner
|
|
110
|
+
},
|
|
111
|
+
metadata: {
|
|
112
|
+
deployBlock: json.metadata.deployBlock,
|
|
113
|
+
deployTimestamp: json.metadata.deployTimestamp,
|
|
114
|
+
deployer: json.metadata.deployer
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
var deployments = {
|
|
119
|
+
31337: parseDeployment(__default, 31337),
|
|
120
|
+
11155111: parseDeployment(__default2, 11155111)
|
|
121
|
+
};
|
|
122
|
+
function getDeployment(chainId) {
|
|
123
|
+
return deployments[chainId] ?? null;
|
|
124
|
+
}
|
|
125
|
+
function getDeploymentOrThrow(chainId) {
|
|
126
|
+
const deployment = getDeployment(chainId);
|
|
127
|
+
if (!deployment) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`No UPD deployment found for chain ${chainId}. Supported chains: ${getSupportedChainIds().join(", ")}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return deployment;
|
|
133
|
+
}
|
|
134
|
+
function hasDeployment(chainId) {
|
|
135
|
+
return chainId in deployments;
|
|
136
|
+
}
|
|
137
|
+
function getSupportedChainIds() {
|
|
138
|
+
return Object.keys(deployments).map(Number);
|
|
139
|
+
}
|
|
140
|
+
function getContractAddress(chainId, contractName) {
|
|
141
|
+
const deployment = getDeployment(chainId);
|
|
142
|
+
if (!deployment) return null;
|
|
143
|
+
return deployment.contracts[contractName] ?? null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/core/client.ts
|
|
147
|
+
var UPDClient = class {
|
|
148
|
+
config;
|
|
149
|
+
deployment;
|
|
150
|
+
oracle;
|
|
151
|
+
constructor(config) {
|
|
152
|
+
this.config = config;
|
|
153
|
+
this.deployment = getDeploymentOrThrow(config.chainId);
|
|
154
|
+
this.oracle = createOracleClient({ oracleUrl: config.oracleUrl });
|
|
155
|
+
}
|
|
156
|
+
// ============================================================
|
|
157
|
+
// READ OPERATIONS
|
|
158
|
+
// ============================================================
|
|
159
|
+
/** Get UPD token balance for an address. */
|
|
160
|
+
async getUPDBalance(address) {
|
|
161
|
+
return this.config.publicClient.readContract({
|
|
162
|
+
address: this.deployment.contracts.UPDToken,
|
|
163
|
+
abi: UPD_TOKEN_ABI,
|
|
164
|
+
functionName: "balanceOf",
|
|
165
|
+
args: [address]
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/** Get total UPD supply. */
|
|
169
|
+
async getTotalSupply() {
|
|
170
|
+
return this.config.publicClient.readContract({
|
|
171
|
+
address: this.deployment.contracts.UPDToken,
|
|
172
|
+
abi: UPD_TOKEN_ABI,
|
|
173
|
+
functionName: "totalSupply"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/** Get system collateral info: total stETH collateral and total UPD supply. */
|
|
177
|
+
async getCollateralInfo() {
|
|
178
|
+
const [totalStEthCollateral, totalUPDSupply] = await Promise.all([
|
|
179
|
+
this.config.publicClient.readContract({
|
|
180
|
+
address: this.deployment.contracts.OvercollateralizationReporter,
|
|
181
|
+
abi: OVERCOLLATERALIZATION_REPORTER_ABI,
|
|
182
|
+
functionName: "totalStEthCollateral"
|
|
183
|
+
}),
|
|
184
|
+
this.getTotalSupply()
|
|
185
|
+
]);
|
|
186
|
+
return { totalStEthCollateral, totalUPDSupply };
|
|
187
|
+
}
|
|
188
|
+
/** Get details for a specific stabilizer position. */
|
|
189
|
+
async getStabilizerPosition(tokenId) {
|
|
190
|
+
const stabNFT = this.deployment.contracts.StabilizerNFT;
|
|
191
|
+
const [posEscrowAddr, stabEscrowAddr, minRatio] = await Promise.all([
|
|
192
|
+
this.config.publicClient.readContract({
|
|
193
|
+
address: stabNFT,
|
|
194
|
+
abi: STABILIZER_NFT_ABI,
|
|
195
|
+
functionName: "positionEscrows",
|
|
196
|
+
args: [tokenId]
|
|
197
|
+
}),
|
|
198
|
+
this.config.publicClient.readContract({
|
|
199
|
+
address: stabNFT,
|
|
200
|
+
abi: STABILIZER_NFT_ABI,
|
|
201
|
+
functionName: "stabilizerEscrows",
|
|
202
|
+
args: [tokenId]
|
|
203
|
+
}),
|
|
204
|
+
this.config.publicClient.readContract({
|
|
205
|
+
address: stabNFT,
|
|
206
|
+
abi: STABILIZER_NFT_ABI,
|
|
207
|
+
functionName: "minCollateralRatios",
|
|
208
|
+
args: [tokenId]
|
|
209
|
+
})
|
|
210
|
+
]);
|
|
211
|
+
const [backedUPDAmount, stETHBalance, unallocatedStETH] = await Promise.all([
|
|
212
|
+
this.config.publicClient.readContract({
|
|
213
|
+
address: posEscrowAddr,
|
|
214
|
+
abi: POSITION_ESCROW_ABI,
|
|
215
|
+
functionName: "backedUPDAmount"
|
|
216
|
+
}),
|
|
217
|
+
this.config.publicClient.readContract({
|
|
218
|
+
address: posEscrowAddr,
|
|
219
|
+
abi: POSITION_ESCROW_ABI,
|
|
220
|
+
functionName: "getCurrentStEthBalance"
|
|
221
|
+
}),
|
|
222
|
+
this.config.publicClient.readContract({
|
|
223
|
+
address: stabEscrowAddr,
|
|
224
|
+
abi: STABILIZER_ESCROW_ABI,
|
|
225
|
+
functionName: "unallocatedStETH"
|
|
226
|
+
})
|
|
227
|
+
]);
|
|
228
|
+
let collateralizationRatio2 = 0n;
|
|
229
|
+
if (backedUPDAmount > 0n && stETHBalance > 0n) {
|
|
230
|
+
collateralizationRatio2 = 0n;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
tokenId,
|
|
234
|
+
positionEscrowAddress: posEscrowAddr,
|
|
235
|
+
stabilizerEscrowAddress: stabEscrowAddr,
|
|
236
|
+
collateralizationRatio: collateralizationRatio2,
|
|
237
|
+
backedUPDAmount,
|
|
238
|
+
stETHBalance,
|
|
239
|
+
unallocatedStETH,
|
|
240
|
+
minCollateralRatio: minRatio
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// ============================================================
|
|
244
|
+
// WRITE OPERATIONS
|
|
245
|
+
// ============================================================
|
|
246
|
+
/**
|
|
247
|
+
* Mint UPD by sending ETH. Requires walletClient.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const attestation = createMockAttestation(2000n * 10n**18n)
|
|
252
|
+
* const hash = await client.mint({
|
|
253
|
+
* to: '0x...',
|
|
254
|
+
* ethAmount: parseEther('1'),
|
|
255
|
+
* priceAttestation: attestation,
|
|
256
|
+
* })
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
async mint(params) {
|
|
260
|
+
const walletClient = this._requireWallet();
|
|
261
|
+
const { to, ethAmount, priceAttestation } = params;
|
|
262
|
+
return walletClient.writeContract({
|
|
263
|
+
address: this.deployment.contracts.StabilizerNFT,
|
|
264
|
+
abi: STABILIZER_NFT_ABI,
|
|
265
|
+
functionName: "mintUPD",
|
|
266
|
+
args: [to, this._toSolidityAttestation(priceAttestation)],
|
|
267
|
+
value: ethAmount,
|
|
268
|
+
chain: walletClient.chain ?? null,
|
|
269
|
+
account: walletClient.account
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Burn UPD to receive stETH. Requires walletClient.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* const attestation = createMockAttestation(2000n * 10n**18n)
|
|
278
|
+
* const hash = await client.burn({
|
|
279
|
+
* updAmount: parseUnits('2000', 18),
|
|
280
|
+
* priceAttestation: attestation,
|
|
281
|
+
* })
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
async burn(params) {
|
|
285
|
+
const walletClient = this._requireWallet();
|
|
286
|
+
const { updAmount, priceAttestation } = params;
|
|
287
|
+
return walletClient.writeContract({
|
|
288
|
+
address: this.deployment.contracts.StabilizerNFT,
|
|
289
|
+
abi: STABILIZER_NFT_ABI,
|
|
290
|
+
functionName: "burnUPD",
|
|
291
|
+
args: [updAmount, this._toSolidityAttestation(priceAttestation)],
|
|
292
|
+
chain: walletClient.chain ?? null,
|
|
293
|
+
account: walletClient.account
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
// ============================================================
|
|
297
|
+
// sUPD OPERATIONS
|
|
298
|
+
// ============================================================
|
|
299
|
+
/** Get sUPD contract info: share value, APY, supply, liability. */
|
|
300
|
+
async getSUPDInfo() {
|
|
301
|
+
const supdAddr = this.deployment.contracts.sUPD;
|
|
302
|
+
if (!supdAddr) throw new Error("sUPD not deployed on this chain");
|
|
303
|
+
const [shareValue, annualYieldBps, totalSupply, totalLiabilityUSD] = await Promise.all([
|
|
304
|
+
this.config.publicClient.readContract({
|
|
305
|
+
address: supdAddr,
|
|
306
|
+
abi: SUPD_ABI,
|
|
307
|
+
functionName: "currentShareValue"
|
|
308
|
+
}),
|
|
309
|
+
this.config.publicClient.readContract({
|
|
310
|
+
address: supdAddr,
|
|
311
|
+
abi: SUPD_ABI,
|
|
312
|
+
functionName: "annualYieldBps"
|
|
313
|
+
}),
|
|
314
|
+
this.config.publicClient.readContract({
|
|
315
|
+
address: supdAddr,
|
|
316
|
+
abi: SUPD_ABI,
|
|
317
|
+
functionName: "totalSupply"
|
|
318
|
+
}),
|
|
319
|
+
this.config.publicClient.readContract({
|
|
320
|
+
address: supdAddr,
|
|
321
|
+
abi: SUPD_ABI,
|
|
322
|
+
functionName: "totalLiabilityUSD"
|
|
323
|
+
})
|
|
324
|
+
]);
|
|
325
|
+
return { shareValue, annualYieldBps, totalSupply, totalLiabilityUSD };
|
|
326
|
+
}
|
|
327
|
+
/** Stake UPD to receive sUPD shares. Requires walletClient. */
|
|
328
|
+
async stakeUPD(params) {
|
|
329
|
+
const walletClient = this._requireWallet();
|
|
330
|
+
const supdAddr = this.deployment.contracts.sUPD;
|
|
331
|
+
if (!supdAddr) throw new Error("sUPD not deployed on this chain");
|
|
332
|
+
return walletClient.writeContract({
|
|
333
|
+
address: supdAddr,
|
|
334
|
+
abi: SUPD_ABI,
|
|
335
|
+
functionName: "stakeUPD",
|
|
336
|
+
args: [params.updAmount, this._toSolidityAttestation(params.priceAttestation)],
|
|
337
|
+
chain: walletClient.chain ?? null,
|
|
338
|
+
account: walletClient.account
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/** Unstake sUPD shares to receive UPD. Requires walletClient. */
|
|
342
|
+
async unstakeUPD(params) {
|
|
343
|
+
const walletClient = this._requireWallet();
|
|
344
|
+
const supdAddr = this.deployment.contracts.sUPD;
|
|
345
|
+
if (!supdAddr) throw new Error("sUPD not deployed on this chain");
|
|
346
|
+
return walletClient.writeContract({
|
|
347
|
+
address: supdAddr,
|
|
348
|
+
abi: SUPD_ABI,
|
|
349
|
+
functionName: "unstakeToUPD",
|
|
350
|
+
args: [params.shares, this._toSolidityAttestation(params.priceAttestation)],
|
|
351
|
+
chain: walletClient.chain ?? null,
|
|
352
|
+
account: walletClient.account
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
// ============================================================
|
|
356
|
+
// CONVENIENCE METHODS
|
|
357
|
+
// ============================================================
|
|
358
|
+
/**
|
|
359
|
+
* Mint UPD by sending ETH, automatically fetching a price attestation from the oracle.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* const hash = await client.mintWithOracle({
|
|
364
|
+
* to: '0x...',
|
|
365
|
+
* ethAmount: parseEther('1'),
|
|
366
|
+
* })
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
async mintWithOracle(params) {
|
|
370
|
+
const attestation = await this.oracle.getEthUsdAttestation();
|
|
371
|
+
return this.mint({ ...params, priceAttestation: attestation });
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Burn UPD to receive stETH, automatically fetching a price attestation from the oracle.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```ts
|
|
378
|
+
* const hash = await client.burnWithOracle({
|
|
379
|
+
* updAmount: parseUnits('2000', 18),
|
|
380
|
+
* })
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
async burnWithOracle(params) {
|
|
384
|
+
const attestation = await this.oracle.getEthUsdAttestation();
|
|
385
|
+
return this.burn({ ...params, priceAttestation: attestation });
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Get system health metrics by combining on-chain collateral data with oracle price.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```ts
|
|
392
|
+
* const health = await client.getSystemHealth()
|
|
393
|
+
* console.log(`System ratio: ${health.systemCollateralRatioBps} bps`)
|
|
394
|
+
* console.log(`Healthy: ${health.isHealthy}`)
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
async getSystemHealth() {
|
|
398
|
+
const [collateral, { price }] = await Promise.all([
|
|
399
|
+
this.getCollateralInfo(),
|
|
400
|
+
this.oracle.getEthUsdPrice()
|
|
401
|
+
]);
|
|
402
|
+
return computeSystemHealth(collateral, price);
|
|
403
|
+
}
|
|
404
|
+
/** Stake UPD, fetching oracle attestation automatically. */
|
|
405
|
+
async stakeUPDWithOracle(params) {
|
|
406
|
+
const attestation = await this.oracle.getEthUsdAttestation();
|
|
407
|
+
return this.stakeUPD({ ...params, priceAttestation: attestation });
|
|
408
|
+
}
|
|
409
|
+
/** Unstake sUPD, fetching oracle attestation automatically. */
|
|
410
|
+
async unstakeUPDWithOracle(params) {
|
|
411
|
+
const attestation = await this.oracle.getEthUsdAttestation();
|
|
412
|
+
return this.unstakeUPD({ ...params, priceAttestation: attestation });
|
|
413
|
+
}
|
|
414
|
+
// ============================================================
|
|
415
|
+
// HELPERS
|
|
416
|
+
// ============================================================
|
|
417
|
+
_requireWallet() {
|
|
418
|
+
if (!this.config.walletClient) {
|
|
419
|
+
throw new Error(
|
|
420
|
+
"UPDClient: walletClient is required for write operations. Pass a walletClient in the config to use mint/burn."
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
return this.config.walletClient;
|
|
424
|
+
}
|
|
425
|
+
_toSolidityAttestation(a) {
|
|
426
|
+
return {
|
|
427
|
+
price: a.price,
|
|
428
|
+
decimals: a.decimals,
|
|
429
|
+
dataTimestamp: a.dataTimestamp,
|
|
430
|
+
assetPair: a.assetPair,
|
|
431
|
+
signature: a.signature
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
function createUPDClient(config) {
|
|
436
|
+
return new UPDClient(config);
|
|
437
|
+
}
|
|
438
|
+
var EIP3009_DOMAIN_VERSION = "1";
|
|
439
|
+
function randomNonce() {
|
|
440
|
+
const bytes = new Uint8Array(32);
|
|
441
|
+
crypto.getRandomValues(bytes);
|
|
442
|
+
return bytesToHex(bytes);
|
|
443
|
+
}
|
|
444
|
+
function eip3009Domain(tokenAddress, chainId, tokenName) {
|
|
445
|
+
return {
|
|
446
|
+
name: tokenName,
|
|
447
|
+
version: EIP3009_DOMAIN_VERSION,
|
|
448
|
+
chainId,
|
|
449
|
+
verifyingContract: tokenAddress
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
async function signTransferAuthorization(params, tokenName = "Universal Private Dollar") {
|
|
453
|
+
const {
|
|
454
|
+
walletClient,
|
|
455
|
+
tokenAddress,
|
|
456
|
+
from,
|
|
457
|
+
to,
|
|
458
|
+
value,
|
|
459
|
+
validAfter = 0n,
|
|
460
|
+
validBefore = BigInt(Math.floor(Date.now() / 1e3) + 3600),
|
|
461
|
+
nonce = randomNonce()
|
|
462
|
+
} = params;
|
|
463
|
+
const chainId = walletClient.chain?.id;
|
|
464
|
+
if (!chainId) throw new Error("walletClient must have a chain configured");
|
|
465
|
+
const signature = await walletClient.signTypedData({
|
|
466
|
+
account: from,
|
|
467
|
+
domain: eip3009Domain(tokenAddress, chainId, tokenName),
|
|
468
|
+
types: {
|
|
469
|
+
TransferWithAuthorization: [
|
|
470
|
+
{ name: "from", type: "address" },
|
|
471
|
+
{ name: "to", type: "address" },
|
|
472
|
+
{ name: "value", type: "uint256" },
|
|
473
|
+
{ name: "validAfter", type: "uint256" },
|
|
474
|
+
{ name: "validBefore", type: "uint256" },
|
|
475
|
+
{ name: "nonce", type: "bytes32" }
|
|
476
|
+
]
|
|
477
|
+
},
|
|
478
|
+
primaryType: "TransferWithAuthorization",
|
|
479
|
+
message: { from, to, value, validAfter, validBefore, nonce }
|
|
480
|
+
});
|
|
481
|
+
const sigBytes = hexToBytes(signature);
|
|
482
|
+
const r = bytesToHex(sigBytes.slice(0, 32));
|
|
483
|
+
const s = bytesToHex(sigBytes.slice(32, 64));
|
|
484
|
+
const v = sigBytes[64];
|
|
485
|
+
return { from, to, value, validAfter, validBefore, nonce, v, r, s };
|
|
486
|
+
}
|
|
487
|
+
async function signReceiveAuthorization(params, tokenName = "Universal Private Dollar") {
|
|
488
|
+
const {
|
|
489
|
+
walletClient,
|
|
490
|
+
tokenAddress,
|
|
491
|
+
from,
|
|
492
|
+
to,
|
|
493
|
+
value,
|
|
494
|
+
validAfter = 0n,
|
|
495
|
+
validBefore = BigInt(Math.floor(Date.now() / 1e3) + 3600),
|
|
496
|
+
nonce = randomNonce()
|
|
497
|
+
} = params;
|
|
498
|
+
const chainId = walletClient.chain?.id;
|
|
499
|
+
if (!chainId) throw new Error("walletClient must have a chain configured");
|
|
500
|
+
const signature = await walletClient.signTypedData({
|
|
501
|
+
account: from,
|
|
502
|
+
domain: eip3009Domain(tokenAddress, chainId, tokenName),
|
|
503
|
+
types: {
|
|
504
|
+
ReceiveWithAuthorization: [
|
|
505
|
+
{ name: "from", type: "address" },
|
|
506
|
+
{ name: "to", type: "address" },
|
|
507
|
+
{ name: "value", type: "uint256" },
|
|
508
|
+
{ name: "validAfter", type: "uint256" },
|
|
509
|
+
{ name: "validBefore", type: "uint256" },
|
|
510
|
+
{ name: "nonce", type: "bytes32" }
|
|
511
|
+
]
|
|
512
|
+
},
|
|
513
|
+
primaryType: "ReceiveWithAuthorization",
|
|
514
|
+
message: { from, to, value, validAfter, validBefore, nonce }
|
|
515
|
+
});
|
|
516
|
+
const sigBytes = hexToBytes(signature);
|
|
517
|
+
const r = bytesToHex(sigBytes.slice(0, 32));
|
|
518
|
+
const s = bytesToHex(sigBytes.slice(32, 64));
|
|
519
|
+
const v = sigBytes[64];
|
|
520
|
+
return { from, to, value, validAfter, validBefore, nonce, v, r, s };
|
|
521
|
+
}
|
|
522
|
+
async function signCancelAuthorization(params, tokenName = "Universal Private Dollar") {
|
|
523
|
+
const { walletClient, tokenAddress, authorizer, nonce } = params;
|
|
524
|
+
const chainId = walletClient.chain?.id;
|
|
525
|
+
if (!chainId) throw new Error("walletClient must have a chain configured");
|
|
526
|
+
const signature = await walletClient.signTypedData({
|
|
527
|
+
account: authorizer,
|
|
528
|
+
domain: eip3009Domain(tokenAddress, chainId, tokenName),
|
|
529
|
+
types: {
|
|
530
|
+
CancelAuthorization: [
|
|
531
|
+
{ name: "authorizer", type: "address" },
|
|
532
|
+
{ name: "nonce", type: "bytes32" }
|
|
533
|
+
]
|
|
534
|
+
},
|
|
535
|
+
primaryType: "CancelAuthorization",
|
|
536
|
+
message: { authorizer, nonce }
|
|
537
|
+
});
|
|
538
|
+
const sigBytes = hexToBytes(signature);
|
|
539
|
+
const r = bytesToHex(sigBytes.slice(0, 32));
|
|
540
|
+
const s = bytesToHex(sigBytes.slice(32, 64));
|
|
541
|
+
const v = sigBytes[64];
|
|
542
|
+
return { authorizer, nonce, v, r, s };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/core/verify.ts
|
|
546
|
+
var ERC1967_IMPL_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
|
547
|
+
async function verifyDeployment(publicClient, deployment) {
|
|
548
|
+
const checks = [];
|
|
549
|
+
await _verifyBytecode(publicClient, checks, "UPDToken", deployment.contracts.UPDToken);
|
|
550
|
+
await _verifyBytecode(publicClient, checks, "PriceOracle", deployment.contracts.PriceOracle);
|
|
551
|
+
await _verifyBytecode(publicClient, checks, "StabilizerNFT", deployment.contracts.StabilizerNFT);
|
|
552
|
+
await _verifyBytecode(publicClient, checks, "Reporter", deployment.contracts.OvercollateralizationReporter);
|
|
553
|
+
await _verifyBytecode(publicClient, checks, "InsuranceEscrow", deployment.contracts.InsuranceEscrow);
|
|
554
|
+
await _verifyProxyImpl(publicClient, checks, "PriceOracle", deployment.contracts.PriceOracle, deployment.implementations.PriceOracle);
|
|
555
|
+
await _verifyProxyImpl(publicClient, checks, "StabilizerNFT", deployment.contracts.StabilizerNFT, deployment.implementations.StabilizerNFT);
|
|
556
|
+
await _verifyProxyImpl(publicClient, checks, "Reporter", deployment.contracts.OvercollateralizationReporter, deployment.implementations.OvercollateralizationReporter);
|
|
557
|
+
await _verifyStabilizerNFTStorage(publicClient, checks, deployment);
|
|
558
|
+
await _verifyReporterStorage(publicClient, checks, deployment);
|
|
559
|
+
await _verifyRole(
|
|
560
|
+
publicClient,
|
|
561
|
+
checks,
|
|
562
|
+
"UPDToken MINTER_ROLE -> StabilizerNFT",
|
|
563
|
+
deployment.contracts.UPDToken,
|
|
564
|
+
UPD_TOKEN_ABI,
|
|
565
|
+
"0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6",
|
|
566
|
+
// keccak256("MINTER_ROLE")
|
|
567
|
+
deployment.contracts.StabilizerNFT
|
|
568
|
+
);
|
|
569
|
+
await _verifyRole(
|
|
570
|
+
publicClient,
|
|
571
|
+
checks,
|
|
572
|
+
"UPDToken BURNER_ROLE -> StabilizerNFT",
|
|
573
|
+
deployment.contracts.UPDToken,
|
|
574
|
+
UPD_TOKEN_ABI,
|
|
575
|
+
"0x3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848",
|
|
576
|
+
// keccak256("BURNER_ROLE")
|
|
577
|
+
deployment.contracts.StabilizerNFT
|
|
578
|
+
);
|
|
579
|
+
const passed = checks.filter((c) => c.passed).length;
|
|
580
|
+
const failed = checks.filter((c) => !c.passed).length;
|
|
581
|
+
return {
|
|
582
|
+
chainId: deployment.chainId,
|
|
583
|
+
checks,
|
|
584
|
+
passed,
|
|
585
|
+
failed,
|
|
586
|
+
allPassed: failed === 0
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
async function _verifyBytecode(client, checks, name, address) {
|
|
590
|
+
try {
|
|
591
|
+
const code = await client.getCode({ address });
|
|
592
|
+
const exists = code !== void 0 && code !== "0x" && code.length > 2;
|
|
593
|
+
checks.push({ name: `${name} bytecode exists`, passed: exists });
|
|
594
|
+
} catch {
|
|
595
|
+
checks.push({ name: `${name} bytecode exists`, passed: false });
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
async function _verifyProxyImpl(client, checks, name, proxyAddress, expectedImpl) {
|
|
599
|
+
try {
|
|
600
|
+
const implSlot = await client.getStorageAt({
|
|
601
|
+
address: proxyAddress,
|
|
602
|
+
slot: ERC1967_IMPL_SLOT
|
|
603
|
+
});
|
|
604
|
+
const actualImpl = ("0x" + (implSlot ?? "0x").slice(26)).toLowerCase();
|
|
605
|
+
const expected = expectedImpl.toLowerCase();
|
|
606
|
+
checks.push({
|
|
607
|
+
name: `${name} proxy -> impl`,
|
|
608
|
+
passed: actualImpl === expected,
|
|
609
|
+
expected: expectedImpl,
|
|
610
|
+
actual: actualImpl
|
|
611
|
+
});
|
|
612
|
+
} catch {
|
|
613
|
+
checks.push({ name: `${name} proxy -> impl`, passed: false, expected: expectedImpl });
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
async function _verifyStabilizerNFTStorage(client, checks, d) {
|
|
617
|
+
try {
|
|
618
|
+
const nft = d.contracts.StabilizerNFT;
|
|
619
|
+
const updToken = await client.readContract({
|
|
620
|
+
address: nft,
|
|
621
|
+
abi: STABILIZER_NFT_ABI,
|
|
622
|
+
functionName: "updToken"
|
|
623
|
+
});
|
|
624
|
+
checks.push({
|
|
625
|
+
name: "StabilizerNFT.updToken",
|
|
626
|
+
passed: updToken.toLowerCase() === d.contracts.UPDToken.toLowerCase(),
|
|
627
|
+
expected: d.contracts.UPDToken,
|
|
628
|
+
actual: updToken
|
|
629
|
+
});
|
|
630
|
+
const oracle = await client.readContract({
|
|
631
|
+
address: nft,
|
|
632
|
+
abi: STABILIZER_NFT_ABI,
|
|
633
|
+
functionName: "oracle"
|
|
634
|
+
});
|
|
635
|
+
checks.push({
|
|
636
|
+
name: "StabilizerNFT.oracle",
|
|
637
|
+
passed: oracle.toLowerCase() === d.contracts.PriceOracle.toLowerCase(),
|
|
638
|
+
expected: d.contracts.PriceOracle,
|
|
639
|
+
actual: oracle
|
|
640
|
+
});
|
|
641
|
+
const stETH = await client.readContract({
|
|
642
|
+
address: nft,
|
|
643
|
+
abi: STABILIZER_NFT_ABI,
|
|
644
|
+
functionName: "stETH"
|
|
645
|
+
});
|
|
646
|
+
checks.push({
|
|
647
|
+
name: "StabilizerNFT.stETH",
|
|
648
|
+
passed: stETH.toLowerCase() === d.external.stETH.toLowerCase(),
|
|
649
|
+
expected: d.external.stETH,
|
|
650
|
+
actual: stETH
|
|
651
|
+
});
|
|
652
|
+
} catch {
|
|
653
|
+
checks.push({ name: "StabilizerNFT storage", passed: false });
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
async function _verifyReporterStorage(client, checks, d) {
|
|
657
|
+
try {
|
|
658
|
+
const reporter = d.contracts.OvercollateralizationReporter;
|
|
659
|
+
const stabNFT = await client.readContract({
|
|
660
|
+
address: reporter,
|
|
661
|
+
abi: OVERCOLLATERALIZATION_REPORTER_ABI,
|
|
662
|
+
functionName: "stabilizerNFTContract"
|
|
663
|
+
});
|
|
664
|
+
checks.push({
|
|
665
|
+
name: "Reporter.stabilizerNFT",
|
|
666
|
+
passed: stabNFT.toLowerCase() === d.contracts.StabilizerNFT.toLowerCase(),
|
|
667
|
+
expected: d.contracts.StabilizerNFT,
|
|
668
|
+
actual: stabNFT
|
|
669
|
+
});
|
|
670
|
+
const updToken = await client.readContract({
|
|
671
|
+
address: reporter,
|
|
672
|
+
abi: OVERCOLLATERALIZATION_REPORTER_ABI,
|
|
673
|
+
functionName: "updToken"
|
|
674
|
+
});
|
|
675
|
+
checks.push({
|
|
676
|
+
name: "Reporter.updToken",
|
|
677
|
+
passed: updToken.toLowerCase() === d.contracts.UPDToken.toLowerCase(),
|
|
678
|
+
expected: d.contracts.UPDToken,
|
|
679
|
+
actual: updToken
|
|
680
|
+
});
|
|
681
|
+
} catch {
|
|
682
|
+
checks.push({ name: "Reporter storage", passed: false });
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
async function _verifyRole(client, checks, name, contractAddress, abi, roleHash, account) {
|
|
686
|
+
try {
|
|
687
|
+
const hasRole = await client.readContract({
|
|
688
|
+
address: contractAddress,
|
|
689
|
+
abi,
|
|
690
|
+
functionName: "hasRole",
|
|
691
|
+
args: [roleHash, account]
|
|
692
|
+
});
|
|
693
|
+
checks.push({ name, passed: hasRole });
|
|
694
|
+
} catch {
|
|
695
|
+
checks.push({ name, passed: false });
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export { UPDClient, createUPDClient, getContractAddress, getDeployment, getDeploymentOrThrow, getSupportedChainIds, hasDeployment, signCancelAuthorization, signReceiveAuthorization, signTransferAuthorization, verifyDeployment };
|
|
700
|
+
//# sourceMappingURL=chunk-R64I3LAO.js.map
|
|
701
|
+
//# sourceMappingURL=chunk-R64I3LAO.js.map
|