@dimes-dot-fi/sdk 1.0.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.
@@ -0,0 +1,670 @@
1
+ import {
2
+ DimesContractError,
3
+ DimesError
4
+ } from "../chunk-LQ5LLBR6.mjs";
5
+
6
+ // src/contract/vault-abi.json
7
+ var vault_abi_default = {
8
+ abi: [
9
+ {
10
+ type: "function",
11
+ name: "createPosition",
12
+ inputs: [
13
+ {
14
+ name: "positionSeed",
15
+ type: "bytes16",
16
+ internalType: "bytes16"
17
+ },
18
+ {
19
+ name: "marketId",
20
+ type: "bytes32",
21
+ internalType: "bytes32"
22
+ },
23
+ {
24
+ name: "tokenId",
25
+ type: "uint256",
26
+ internalType: "uint256"
27
+ },
28
+ {
29
+ name: "collateralUsdcUnits",
30
+ type: "uint256",
31
+ internalType: "uint256"
32
+ },
33
+ {
34
+ name: "leverageBps",
35
+ type: "uint32",
36
+ internalType: "uint32"
37
+ },
38
+ {
39
+ name: "notionalUsdcUnits",
40
+ type: "uint256",
41
+ internalType: "uint256"
42
+ },
43
+ {
44
+ name: "originationFeeBps",
45
+ type: "uint16",
46
+ internalType: "uint16"
47
+ },
48
+ {
49
+ name: "lifetimeFeeAprBps",
50
+ type: "uint16",
51
+ internalType: "uint16"
52
+ },
53
+ {
54
+ name: "liquidationFeeBps",
55
+ type: "uint16",
56
+ internalType: "uint16"
57
+ },
58
+ {
59
+ name: "venueFeeUsdcUnits",
60
+ type: "uint256",
61
+ internalType: "uint256"
62
+ },
63
+ {
64
+ name: "signature",
65
+ type: "bytes",
66
+ internalType: "bytes"
67
+ },
68
+ {
69
+ name: "signatureExpiry",
70
+ type: "uint256",
71
+ internalType: "uint256"
72
+ }
73
+ ],
74
+ outputs: [],
75
+ stateMutability: "nonpayable"
76
+ },
77
+ {
78
+ type: "function",
79
+ name: "requestClose",
80
+ inputs: [
81
+ {
82
+ name: "positionKey",
83
+ type: "bytes32",
84
+ internalType: "bytes32"
85
+ }
86
+ ],
87
+ outputs: [],
88
+ stateMutability: "nonpayable"
89
+ },
90
+ {
91
+ type: "event",
92
+ name: "CloseRequested",
93
+ inputs: [
94
+ {
95
+ name: "positionKey",
96
+ type: "bytes32",
97
+ indexed: true,
98
+ internalType: "bytes32"
99
+ },
100
+ {
101
+ name: "owner",
102
+ type: "address",
103
+ indexed: true,
104
+ internalType: "address"
105
+ },
106
+ {
107
+ name: "state",
108
+ type: "uint8",
109
+ indexed: false,
110
+ internalType: "uint8"
111
+ },
112
+ {
113
+ name: "timestamp",
114
+ type: "uint256",
115
+ indexed: false,
116
+ internalType: "uint256"
117
+ }
118
+ ],
119
+ anonymous: false
120
+ },
121
+ {
122
+ type: "event",
123
+ name: "PositionCreated",
124
+ inputs: [
125
+ {
126
+ name: "positionKey",
127
+ type: "bytes32",
128
+ indexed: true,
129
+ internalType: "bytes32"
130
+ },
131
+ {
132
+ name: "positionSeed",
133
+ type: "bytes16",
134
+ indexed: false,
135
+ internalType: "bytes16"
136
+ },
137
+ {
138
+ name: "owner",
139
+ type: "address",
140
+ indexed: true,
141
+ internalType: "address"
142
+ },
143
+ {
144
+ name: "marketId",
145
+ type: "bytes32",
146
+ indexed: true,
147
+ internalType: "bytes32"
148
+ },
149
+ {
150
+ name: "tokenId",
151
+ type: "uint256",
152
+ indexed: false,
153
+ internalType: "uint256"
154
+ },
155
+ {
156
+ name: "collateralUsdcUnits",
157
+ type: "uint256",
158
+ indexed: false,
159
+ internalType: "uint256"
160
+ },
161
+ {
162
+ name: "leverageBps",
163
+ type: "uint32",
164
+ indexed: false,
165
+ internalType: "uint32"
166
+ },
167
+ {
168
+ name: "notionalUsdcUnits",
169
+ type: "uint256",
170
+ indexed: false,
171
+ internalType: "uint256"
172
+ },
173
+ {
174
+ name: "originationFeeUsdcUnits",
175
+ type: "uint256",
176
+ indexed: false,
177
+ internalType: "uint256"
178
+ },
179
+ {
180
+ name: "venueFeeUsdcUnits",
181
+ type: "uint256",
182
+ indexed: false,
183
+ internalType: "uint256"
184
+ },
185
+ {
186
+ name: "originationFeeBps",
187
+ type: "uint16",
188
+ indexed: false,
189
+ internalType: "uint16"
190
+ },
191
+ {
192
+ name: "lifetimeFeeAprBps",
193
+ type: "uint16",
194
+ indexed: false,
195
+ internalType: "uint16"
196
+ },
197
+ {
198
+ name: "liquidationFeeBps",
199
+ type: "uint16",
200
+ indexed: false,
201
+ internalType: "uint16"
202
+ },
203
+ {
204
+ name: "state",
205
+ type: "uint8",
206
+ indexed: false,
207
+ internalType: "uint8"
208
+ },
209
+ {
210
+ name: "timestamp",
211
+ type: "uint256",
212
+ indexed: false,
213
+ internalType: "uint256"
214
+ }
215
+ ],
216
+ anonymous: false
217
+ },
218
+ {
219
+ type: "error",
220
+ name: "CancelDelayNotElapsed",
221
+ inputs: []
222
+ },
223
+ {
224
+ type: "error",
225
+ name: "CollateralBelowMinimum",
226
+ inputs: []
227
+ },
228
+ {
229
+ type: "error",
230
+ name: "ECDSAInvalidSignature",
231
+ inputs: []
232
+ },
233
+ {
234
+ type: "error",
235
+ name: "ECDSAInvalidSignatureLength",
236
+ inputs: [
237
+ {
238
+ name: "length",
239
+ type: "uint256",
240
+ internalType: "uint256"
241
+ }
242
+ ]
243
+ },
244
+ {
245
+ type: "error",
246
+ name: "ECDSAInvalidSignatureS",
247
+ inputs: [
248
+ {
249
+ name: "s",
250
+ type: "bytes32",
251
+ internalType: "bytes32"
252
+ }
253
+ ]
254
+ },
255
+ {
256
+ type: "error",
257
+ name: "EmergencyOnlyMode",
258
+ inputs: []
259
+ },
260
+ {
261
+ type: "error",
262
+ name: "EnforcedPause",
263
+ inputs: []
264
+ },
265
+ {
266
+ type: "error",
267
+ name: "FailedCall",
268
+ inputs: []
269
+ },
270
+ {
271
+ type: "error",
272
+ name: "FeeRateExceedsMaximum",
273
+ inputs: []
274
+ },
275
+ {
276
+ type: "error",
277
+ name: "InsufficientCapital",
278
+ inputs: []
279
+ },
280
+ {
281
+ type: "error",
282
+ name: "InvalidLeverage",
283
+ inputs: []
284
+ },
285
+ {
286
+ type: "error",
287
+ name: "InvalidNotional",
288
+ inputs: []
289
+ },
290
+ {
291
+ type: "error",
292
+ name: "InvalidPositionState",
293
+ inputs: []
294
+ },
295
+ {
296
+ type: "error",
297
+ name: "InvalidSignature",
298
+ inputs: []
299
+ },
300
+ {
301
+ type: "error",
302
+ name: "MarketCapitalExceeded",
303
+ inputs: []
304
+ },
305
+ {
306
+ type: "error",
307
+ name: "NewPositionsPaused",
308
+ inputs: []
309
+ },
310
+ {
311
+ type: "error",
312
+ name: "NoTokensToClose",
313
+ inputs: []
314
+ },
315
+ {
316
+ type: "error",
317
+ name: "NotPositionOwner",
318
+ inputs: []
319
+ },
320
+ {
321
+ type: "error",
322
+ name: "PositionAlreadyExists",
323
+ inputs: []
324
+ },
325
+ {
326
+ type: "error",
327
+ name: "PositionNotFound",
328
+ inputs: []
329
+ },
330
+ {
331
+ type: "error",
332
+ name: "SafeERC20FailedOperation",
333
+ inputs: [
334
+ {
335
+ name: "token",
336
+ type: "address",
337
+ internalType: "address"
338
+ }
339
+ ]
340
+ },
341
+ {
342
+ type: "error",
343
+ name: "ServerHalted",
344
+ inputs: []
345
+ },
346
+ {
347
+ type: "error",
348
+ name: "SignatureExpired",
349
+ inputs: []
350
+ },
351
+ {
352
+ type: "error",
353
+ name: "TokenCapitalExceeded",
354
+ inputs: []
355
+ },
356
+ {
357
+ type: "error",
358
+ name: "TotalCapitalExceeded",
359
+ inputs: []
360
+ },
361
+ {
362
+ type: "error",
363
+ name: "UserActionsPaused",
364
+ inputs: []
365
+ },
366
+ {
367
+ type: "error",
368
+ name: "UserCapitalExceeded",
369
+ inputs: []
370
+ },
371
+ {
372
+ type: "error",
373
+ name: "ZeroAddress",
374
+ inputs: []
375
+ },
376
+ {
377
+ type: "error",
378
+ name: "ZeroAmount",
379
+ inputs: []
380
+ }
381
+ ]
382
+ };
383
+
384
+ // src/contract/abi.ts
385
+ var vaultAbi = vault_abi_default.abi;
386
+ var erc20Abi = [
387
+ {
388
+ name: "approve",
389
+ type: "function",
390
+ stateMutability: "nonpayable",
391
+ inputs: [
392
+ { name: "spender", type: "address" },
393
+ { name: "amount", type: "uint256" }
394
+ ],
395
+ outputs: [{ name: "", type: "bool" }]
396
+ },
397
+ {
398
+ name: "allowance",
399
+ type: "function",
400
+ stateMutability: "view",
401
+ inputs: [
402
+ { name: "owner", type: "address" },
403
+ { name: "spender", type: "address" }
404
+ ],
405
+ outputs: [{ name: "", type: "uint256" }]
406
+ }
407
+ ];
408
+
409
+ // src/contract/evm.ts
410
+ import { isHex, isAddress, getAddress } from "viem";
411
+ function toHex(value) {
412
+ if (!isHex(value)) {
413
+ const HEX_PREVIEW_LENGTH = 10;
414
+ throw new DimesError(
415
+ "invalid_hex",
416
+ `Expected hex string starting with 0x, got: ${value.slice(0, HEX_PREVIEW_LENGTH)}`
417
+ );
418
+ }
419
+ return value;
420
+ }
421
+ function toAddress(value) {
422
+ if (!isAddress(value)) {
423
+ throw new DimesError("invalid_address", `Invalid EVM address: ${value}`);
424
+ }
425
+ return getAddress(value);
426
+ }
427
+
428
+ // src/contract/build-tx.ts
429
+ function buildCreatePositionTx(offer) {
430
+ return {
431
+ address: toAddress(offer.polygonVaultContractAddress),
432
+ abi: vaultAbi,
433
+ functionName: "createPosition",
434
+ args: [
435
+ toHex(offer.positionSeedHex),
436
+ toHex(offer.polymarketMarketId),
437
+ BigInt(offer.polymarketTokenId),
438
+ BigInt(offer.collateralUsdcUnits),
439
+ offer.leverageBps,
440
+ BigInt(offer.notionalUsdcUnits),
441
+ offer.originationFeeBps,
442
+ offer.lifetimeFeeAprBps,
443
+ offer.liquidationFeeBps,
444
+ BigInt(offer.expectedOpenTradingFeeUsdcUnits),
445
+ toHex(offer.contractSignature),
446
+ BigInt(offer.signatureExpiry)
447
+ ]
448
+ };
449
+ }
450
+ function buildApproveTx(usdcAddress, spender, amount) {
451
+ return {
452
+ address: usdcAddress,
453
+ abi: erc20Abi,
454
+ functionName: "approve",
455
+ args: [spender, amount]
456
+ };
457
+ }
458
+ function buildRequestCloseTx(vaultAddress, positionKey) {
459
+ return {
460
+ address: vaultAddress,
461
+ abi: vaultAbi,
462
+ functionName: "requestClose",
463
+ args: [positionKey]
464
+ };
465
+ }
466
+
467
+ // src/contract/verify-signature.ts
468
+ import { recoverTypedDataAddress, getAddress as getAddress2 } from "viem";
469
+ var createPositionTypes = {
470
+ CreatePosition: [
471
+ { name: "positionSeed", type: "bytes16" },
472
+ { name: "user", type: "address" },
473
+ { name: "marketId", type: "bytes32" },
474
+ { name: "tokenId", type: "uint256" },
475
+ { name: "collateralUsdcUnits", type: "uint256" },
476
+ { name: "leverageBps", type: "uint32" },
477
+ { name: "notionalUsdcUnits", type: "uint256" },
478
+ { name: "originationFeeBps", type: "uint16" },
479
+ { name: "lifetimeFeeAprBps", type: "uint16" },
480
+ { name: "liquidationFeeBps", type: "uint16" },
481
+ { name: "venueFeeUsdcUnits", type: "uint256" },
482
+ { name: "signatureExpiry", type: "uint256" }
483
+ ]
484
+ };
485
+ async function recoverCreatePositionSigner(offer, userAddress) {
486
+ return recoverTypedDataAddress({
487
+ domain: {
488
+ name: "LeveragedPredictionVaultV1",
489
+ version: "1",
490
+ chainId: Number(offer.evmChainId),
491
+ verifyingContract: toAddress(offer.polygonVaultContractAddress)
492
+ },
493
+ types: createPositionTypes,
494
+ primaryType: "CreatePosition",
495
+ message: {
496
+ positionSeed: toHex(offer.positionSeedHex),
497
+ user: userAddress,
498
+ marketId: toHex(offer.polymarketMarketId),
499
+ tokenId: BigInt(offer.polymarketTokenId),
500
+ collateralUsdcUnits: BigInt(offer.collateralUsdcUnits),
501
+ leverageBps: offer.leverageBps,
502
+ notionalUsdcUnits: BigInt(offer.notionalUsdcUnits),
503
+ originationFeeBps: offer.originationFeeBps,
504
+ lifetimeFeeAprBps: offer.lifetimeFeeAprBps,
505
+ liquidationFeeBps: offer.liquidationFeeBps,
506
+ venueFeeUsdcUnits: BigInt(offer.expectedOpenTradingFeeUsdcUnits),
507
+ signatureExpiry: BigInt(offer.signatureExpiry)
508
+ },
509
+ signature: toHex(offer.contractSignature)
510
+ });
511
+ }
512
+ function resolveExpectedSigner(apiSigner) {
513
+ return apiSigner ? getAddress2(apiSigner) : void 0;
514
+ }
515
+ var contractInfoCache = /* @__PURE__ */ new WeakMap();
516
+ async function verifyOfferSignature(client, offer, userAddress) {
517
+ let info = contractInfoCache.get(client);
518
+ if (!info) {
519
+ info = await client.getContractInfo();
520
+ contractInfoCache.set(client, info);
521
+ }
522
+ const recoveredSigner = await recoverCreatePositionSigner(offer, userAddress);
523
+ const expectedSigner = getAddress2(info.polygonSignerAddress);
524
+ if (recoveredSigner !== expectedSigner) {
525
+ throw new DimesContractError(
526
+ "invalid_signer",
527
+ `Recovered signer ${recoveredSigner} does not match expected authority ${expectedSigner}`
528
+ );
529
+ }
530
+ }
531
+
532
+ // src/contract/error-messages.ts
533
+ import {
534
+ BaseError,
535
+ ContractFunctionRevertedError,
536
+ ContractFunctionExecutionError,
537
+ UserRejectedRequestError,
538
+ formatUnits
539
+ } from "viem";
540
+ var USDC_DECIMALS = 6;
541
+ var BPS_PER_PCT = 100;
542
+ var fmtUsdc = (v) => typeof v === "bigint" ? `${formatUnits(v, USDC_DECIMALS)} USDC` : String(v);
543
+ var BPS_DECIMAL_PLACES = 2;
544
+ var fmtBps = (v) => {
545
+ if (typeof v === "bigint") {
546
+ return `${(Number(v) / BPS_PER_PCT).toFixed(BPS_DECIMAL_PLACES)}%`;
547
+ }
548
+ if (typeof v === "number") {
549
+ return `${(v / BPS_PER_PCT).toFixed(BPS_DECIMAL_PLACES)}%`;
550
+ }
551
+ return String(v);
552
+ };
553
+ var friendlyByErrorName = {
554
+ InvalidSignature: () => "Quote signature is invalid. Refresh and try again.",
555
+ SignatureExpired: () => "Quote has expired. Refresh and try again.",
556
+ TokenIdMismatch: () => "Quote token mismatch. Refresh and try again.",
557
+ InsufficientCapital: () => "Pool is short on capital right now. Try a smaller size.",
558
+ UserCapitalExceeded: (a) => a && a.length >= 4 ? `You'd exceed your account cap of ${fmtUsdc(a[3])} (current ${fmtUsdc(a[1])}, requested ${fmtUsdc(a[2])}).` : "You'd exceed your account capital cap.",
559
+ MarketCapitalExceeded: (a) => a && a.length >= 4 ? `Market is at capacity (cap ${fmtUsdc(a[3])}, requested ${fmtUsdc(a[2])}).` : "Market is at capacity.",
560
+ TotalCapitalExceeded: (a) => a && a.length >= 3 ? `Protocol is at capacity (cap ${fmtUsdc(a[2])}, requested ${fmtUsdc(a[1])}).` : "Protocol is at capacity.",
561
+ TokenCapitalExceeded: () => "This outcome is at capacity.",
562
+ InvalidLeverage: (a) => a && a.length >= 1 ? `Invalid leverage (${fmtBps(a[0])}).` : "Invalid leverage.",
563
+ CollateralBelowMinimum: (a) => a && a.length >= 2 ? `Collateral ${fmtUsdc(a[0])} is below the minimum of ${fmtUsdc(a[1])}.` : "Collateral is below the minimum.",
564
+ InvalidCollateral: (a) => a && a.length >= 1 ? `Invalid collateral amount (${fmtUsdc(a[0])}).` : "Invalid collateral amount.",
565
+ InvalidNotional: (a) => a && a.length >= 1 ? `Invalid notional (${fmtUsdc(a[0])}).` : "Invalid notional.",
566
+ ZeroAmount: () => "Amount cannot be zero.",
567
+ ZeroAddress: () => "Address cannot be zero.",
568
+ PositionAlreadyExists: () => "A position with this seed already exists.",
569
+ PositionNotFound: () => "Position not found.",
570
+ PositionAlreadyFinalized: () => "Position is already finalized.",
571
+ InvalidPositionState: () => "Position is in the wrong state for this action.",
572
+ NotPositionOwner: () => "You're not the owner of this position.",
573
+ NewPositionsPaused: () => "New positions are paused.",
574
+ UserActionsPaused: () => "User actions are temporarily paused.",
575
+ EmergencyOnlyMode: () => "Protocol is in emergency-only mode.",
576
+ ServerHalted: () => "Protocol is halted. Try again later.",
577
+ NoTokensToClose: () => "Position has no tokens to close.",
578
+ InvalidProceeds: () => "Invalid proceeds amount.",
579
+ InvalidMarketOutcome: () => "Invalid market outcome.",
580
+ InvalidTokensToSell: () => "Invalid token amount to sell.",
581
+ InvalidTargetLeverage: () => "Target leverage must be lower than current.",
582
+ CancelDelayNotElapsed: () => "Cancel delay has not elapsed yet.",
583
+ CancelDelayTooLong: () => "Cancel delay exceeds the maximum allowed.",
584
+ SafeERC20FailedOperation: () => "Token transfer failed.",
585
+ UnauthorizedTokenSender: () => "Unauthorized token sender.",
586
+ CannotRescueOperationalToken: () => "Cannot rescue operational tokens.",
587
+ ERC20InsufficientBalance: () => "Insufficient USDC balance.",
588
+ ERC20InsufficientAllowance: () => "Insufficient USDC allowance. Approve and try again."
589
+ };
590
+ var friendlyBySelector = {
591
+ "0xe450d38c": "Insufficient USDC balance.",
592
+ "0xfb8f41b2": "Insufficient USDC allowance. Approve and try again.",
593
+ "0x96c6fd1e": "Invalid token sender (zero address).",
594
+ "0xec442f05": "Invalid token receiver (zero address).",
595
+ "0xe602df05": "Invalid token approver (zero address).",
596
+ "0x94280d62": "Invalid spender (zero address).",
597
+ "0x03dee4c5": "Insufficient token balance for transfer.",
598
+ "0x01a83514": "Invalid ERC-1155 sender (zero address).",
599
+ "0x57f447ce": "Invalid ERC-1155 receiver.",
600
+ "0xe237d922": "Missing ERC-1155 approval.",
601
+ "0x3e31884e": "Invalid ERC-1155 approver (zero address).",
602
+ "0xced3e100": "Invalid ERC-1155 operator (zero address).",
603
+ "0x5b059991": "ERC-1155 array length mismatch."
604
+ };
605
+ var HEX_SELECTOR_LENGTH = 10;
606
+ function humanizeName(name) {
607
+ const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
608
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
609
+ }
610
+ function firstLine(s) {
611
+ const i = s.indexOf("\n");
612
+ return (i === -1 ? s : s.slice(0, i)).trim();
613
+ }
614
+ function formatContractError(err) {
615
+ if (!err) {
616
+ return new DimesContractError("unknown", "Transaction failed.");
617
+ }
618
+ if (err instanceof BaseError) {
619
+ const rejected = err.walk((e) => e instanceof UserRejectedRequestError);
620
+ if (rejected) {
621
+ return new DimesContractError("UserRejected", "Transaction rejected in wallet.");
622
+ }
623
+ const reverted = err.walk(
624
+ (e) => e instanceof ContractFunctionRevertedError
625
+ );
626
+ if (reverted) {
627
+ const errorName = reverted.data?.errorName;
628
+ const args = reverted.data?.args;
629
+ if (errorName) {
630
+ const fmt = friendlyByErrorName[errorName];
631
+ const message = fmt ? fmt(args) : humanizeName(errorName);
632
+ return new DimesContractError(errorName, message);
633
+ }
634
+ const raw = reverted.raw;
635
+ const selector = raw && raw.length >= HEX_SELECTOR_LENGTH ? raw.slice(0, HEX_SELECTOR_LENGTH) : void 0;
636
+ const selectorMessage = selector ? friendlyBySelector[selector] : void 0;
637
+ return new DimesContractError(
638
+ selector ?? "unknown_revert",
639
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
640
+ selectorMessage ?? reverted.shortMessage ?? "Contract reverted."
641
+ );
642
+ }
643
+ const exec = err.walk((e) => e instanceof ContractFunctionExecutionError);
644
+ if (exec?.shortMessage) {
645
+ return new DimesContractError("execution_error", firstLine(exec.shortMessage));
646
+ }
647
+ if (err.shortMessage) {
648
+ return new DimesContractError("base_error", firstLine(err.shortMessage));
649
+ }
650
+ return new DimesContractError("base_error", firstLine(err.message));
651
+ }
652
+ if (err instanceof Error) {
653
+ return new DimesContractError("error", firstLine(err.message));
654
+ }
655
+ return new DimesContractError("unknown", "Transaction failed.");
656
+ }
657
+ export {
658
+ buildApproveTx,
659
+ buildCreatePositionTx,
660
+ buildRequestCloseTx,
661
+ erc20Abi,
662
+ formatContractError,
663
+ recoverCreatePositionSigner,
664
+ resolveExpectedSigner,
665
+ toAddress,
666
+ toHex,
667
+ vaultAbi,
668
+ verifyOfferSignature
669
+ };
670
+ //# sourceMappingURL=index.mjs.map