levr-sdk 0.0.1 → 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 +109 -13
- package/dist/esm/abis/LevrFactory_v1.js +63 -0
- package/dist/esm/abis/LevrGovernor_v1.js +15 -24
- package/dist/esm/abis/LevrStaking_v1.js +7 -1
- package/dist/esm/abis/StateView.js +149 -0
- package/dist/esm/abis/V3QuoterV2.js +26 -0
- package/dist/esm/abis/index.js +4 -0
- package/dist/esm/balance.js +48 -4
- package/dist/esm/balance.js.map +1 -1
- package/dist/esm/client/hook/index.js +14 -3
- package/dist/esm/client/hook/index.js.map +1 -1
- package/dist/esm/client/hook/use-fee-receivers.js +11 -30
- package/dist/esm/client/hook/use-fee-receivers.js.map +1 -1
- package/dist/esm/client/hook/use-governance.js +20 -274
- package/dist/esm/client/hook/use-governance.js.map +1 -1
- package/dist/esm/client/hook/use-pool.js +26 -0
- package/dist/esm/client/hook/use-pool.js.map +1 -0
- package/dist/esm/client/hook/use-prepare.js +6 -3
- package/dist/esm/client/hook/use-prepare.js.map +1 -1
- package/dist/esm/client/hook/use-project.js +28 -16
- package/dist/esm/client/hook/use-project.js.map +1 -1
- package/dist/esm/client/hook/use-proposal.js +48 -0
- package/dist/esm/client/hook/use-proposal.js.map +1 -0
- package/dist/esm/client/hook/use-stake.js +29 -215
- package/dist/esm/client/hook/use-stake.js.map +1 -1
- package/dist/esm/client/hook/use-swap.js +23 -19
- package/dist/esm/client/hook/use-swap.js.map +1 -1
- package/dist/esm/client/hook/use-user.js +25 -0
- package/dist/esm/client/hook/use-user.js.map +1 -0
- package/dist/esm/client/levr-provider.js +73 -108
- package/dist/esm/client/levr-provider.js.map +1 -1
- package/dist/esm/client/query-keys.js +10 -22
- package/dist/esm/client/query-keys.js.map +1 -1
- package/dist/esm/constants.js +42 -1
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/fee-receivers.js +28 -13
- package/dist/esm/fee-receivers.js.map +1 -1
- package/dist/esm/governance.js +35 -399
- package/dist/esm/governance.js.map +1 -1
- package/dist/esm/index.js +7 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pool-key.js +152 -0
- package/dist/esm/pool-key.js.map +1 -0
- package/dist/esm/pool.js +70 -0
- package/dist/esm/pool.js.map +1 -0
- package/dist/esm/project.js +415 -118
- package/dist/esm/project.js.map +1 -1
- package/dist/esm/proposal.js +156 -0
- package/dist/esm/proposal.js.map +1 -0
- package/dist/esm/quote/index.js +98 -0
- package/dist/esm/quote/index.js.map +1 -0
- package/dist/esm/quote/v3.js +62 -0
- package/dist/esm/quote/v3.js.map +1 -0
- package/dist/esm/quote/v4.js +228 -0
- package/dist/esm/quote/v4.js.map +1 -0
- package/dist/esm/stake.js +34 -237
- package/dist/esm/stake.js.map +1 -1
- package/dist/esm/treasury.js +162 -0
- package/dist/esm/treasury.js.map +1 -0
- package/dist/esm/usd-price.js +149 -0
- package/dist/esm/usd-price.js.map +1 -0
- package/dist/esm/user.js +153 -0
- package/dist/esm/user.js.map +1 -0
- package/dist/esm/util.js +45 -1
- package/dist/esm/util.js.map +1 -1
- package/dist/types/abis/LevrFactory_v1.d.ts +48 -0
- package/dist/types/abis/LevrGovernor_v1.d.ts +12 -18
- package/dist/types/abis/LevrStaking_v1.d.ts +5 -1
- package/dist/types/abis/StateView.d.ts +278 -0
- package/dist/types/abis/V3QuoterV2.d.ts +39 -0
- package/dist/types/abis/index.d.ts +2 -0
- package/dist/types/balance.d.ts +19 -6
- package/dist/types/balance.d.ts.map +1 -1
- package/dist/types/client/hook/index.d.ts +12 -4
- package/dist/types/client/hook/index.d.ts.map +1 -1
- package/dist/types/client/hook/use-fee-receivers.d.ts +6 -16
- package/dist/types/client/hook/use-fee-receivers.d.ts.map +1 -1
- package/dist/types/client/hook/use-governance.d.ts +4 -93
- package/dist/types/client/hook/use-governance.d.ts.map +1 -1
- package/dist/types/client/hook/use-pool.d.ts +12 -0
- package/dist/types/client/hook/use-pool.d.ts.map +1 -0
- package/dist/types/client/hook/use-prepare.d.ts +1 -2
- package/dist/types/client/hook/use-prepare.d.ts.map +1 -1
- package/dist/types/client/hook/use-project.d.ts +8 -1
- package/dist/types/client/hook/use-project.d.ts.map +1 -1
- package/dist/types/client/hook/use-proposal.d.ts +18 -0
- package/dist/types/client/hook/use-proposal.d.ts.map +1 -0
- package/dist/types/client/hook/use-stake.d.ts +14 -168
- package/dist/types/client/hook/use-stake.d.ts.map +1 -1
- package/dist/types/client/hook/use-swap.d.ts +5 -9
- package/dist/types/client/hook/use-swap.d.ts.map +1 -1
- package/dist/types/client/hook/use-user.d.ts +13 -0
- package/dist/types/client/hook/use-user.d.ts.map +1 -0
- package/dist/types/client/levr-provider.d.ts +41 -68
- package/dist/types/client/levr-provider.d.ts.map +1 -1
- package/dist/types/client/query-keys.d.ts +14 -26
- package/dist/types/client/query-keys.d.ts.map +1 -1
- package/dist/types/constants.d.ts +18 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/fee-receivers.d.ts +29 -2
- package/dist/types/fee-receivers.d.ts.map +1 -1
- package/dist/types/governance.d.ts +8 -108
- package/dist/types/governance.d.ts.map +1 -1
- package/dist/types/index.d.ts +7 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/pool-key.d.ts +121 -0
- package/dist/types/pool-key.d.ts.map +1 -0
- package/dist/types/pool.d.ts +590 -0
- package/dist/types/pool.d.ts.map +1 -0
- package/dist/types/project.d.ts +61 -11
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/proposal.d.ts +1161 -0
- package/dist/types/proposal.d.ts.map +1 -0
- package/dist/types/quote/index.d.ts +97 -0
- package/dist/types/quote/index.d.ts.map +1 -0
- package/dist/types/quote/v3.d.ts +78 -0
- package/dist/types/quote/v3.d.ts.map +1 -0
- package/dist/types/quote/v4.d.ts +95 -0
- package/dist/types/quote/v4.d.ts.map +1 -0
- package/dist/types/stake.d.ts +41 -87
- package/dist/types/stake.d.ts.map +1 -1
- package/dist/types/treasury.d.ts +16 -0
- package/dist/types/treasury.d.ts.map +1 -0
- package/dist/types/types.d.ts +15 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/usd-price.d.ts +141 -0
- package/dist/types/usd-price.d.ts.map +1 -0
- package/dist/types/user.d.ts +188 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/util.d.ts +17 -0
- package/dist/types/util.d.ts.map +1 -1
- package/package.json +5 -2
- package/dist/esm/client/hook/use-balance.js +0 -56
- package/dist/esm/client/hook/use-balance.js.map +0 -1
- package/dist/esm/client/hook/use-projects.js +0 -22
- package/dist/esm/client/hook/use-projects.js.map +0 -1
- package/dist/esm/client/hook/use-proposals.js +0 -31
- package/dist/esm/client/hook/use-proposals.js.map +0 -1
- package/dist/esm/projects.js +0 -212
- package/dist/esm/projects.js.map +0 -1
- package/dist/esm/proposals.js +0 -98
- package/dist/esm/proposals.js.map +0 -1
- package/dist/esm/quote-v4.js +0 -169
- package/dist/esm/quote-v4.js.map +0 -1
- package/dist/types/client/hook/use-balance.d.ts +0 -18
- package/dist/types/client/hook/use-balance.d.ts.map +0 -1
- package/dist/types/client/hook/use-projects.d.ts +0 -6
- package/dist/types/client/hook/use-projects.d.ts.map +0 -1
- package/dist/types/client/hook/use-proposals.d.ts +0 -16
- package/dist/types/client/hook/use-proposals.d.ts.map +0 -1
- package/dist/types/projects.d.ts +0 -20
- package/dist/types/projects.d.ts.map +0 -1
- package/dist/types/proposals.d.ts +0 -20
- package/dist/types/proposals.d.ts.map +0 -1
- package/dist/types/quote-v4.d.ts +0 -54
- package/dist/types/quote-v4.d.ts.map +0 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { formatUnits } from "viem";
|
|
2
|
+
import { LevrGovernor_v1 } from "./abis/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Get call data contracts for a single proposal
|
|
5
|
+
* Returns 1-2 contract configs: getProposal (includes state, meetsQuorum, meetsApproval), [getVoteReceipt]
|
|
6
|
+
*/
|
|
7
|
+
export function proposalCallData(governorAddress, proposalId, userAddress) {
|
|
8
|
+
const baseContracts = [
|
|
9
|
+
{
|
|
10
|
+
address: governorAddress,
|
|
11
|
+
abi: LevrGovernor_v1,
|
|
12
|
+
functionName: "getProposal",
|
|
13
|
+
args: [proposalId],
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
// Add vote receipt if userAddress is provided
|
|
17
|
+
if (userAddress) {
|
|
18
|
+
return [
|
|
19
|
+
...baseContracts,
|
|
20
|
+
{
|
|
21
|
+
address: governorAddress,
|
|
22
|
+
abi: LevrGovernor_v1,
|
|
23
|
+
functionName: "getVoteReceipt",
|
|
24
|
+
args: [proposalId, userAddress],
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
return baseContracts;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse proposal data from multicall results
|
|
32
|
+
* Expects 1-2 results: [getProposal (includes state, meetsQuorum, meetsApproval), [getVoteReceipt]]
|
|
33
|
+
*/
|
|
34
|
+
export function parseProposalData(results, tokenDecimals, pricing) {
|
|
35
|
+
const proposalData = results[0];
|
|
36
|
+
// State, meetsQuorum, and meetsApproval are now included in the proposal data
|
|
37
|
+
const state = proposalData.state;
|
|
38
|
+
const meetsQuorum = proposalData.meetsQuorum;
|
|
39
|
+
const meetsApproval = proposalData.meetsApproval;
|
|
40
|
+
const voteReceiptData = results[1];
|
|
41
|
+
const amountFormatted = formatUnits(proposalData.amount, tokenDecimals);
|
|
42
|
+
const yesVotesFormatted = formatUnits(proposalData.yesVotes, tokenDecimals);
|
|
43
|
+
const noVotesFormatted = formatUnits(proposalData.noVotes, tokenDecimals);
|
|
44
|
+
const tokenPrice = pricing ? parseFloat(pricing.tokenUsd) : null;
|
|
45
|
+
return {
|
|
46
|
+
id: proposalData.id,
|
|
47
|
+
proposalType: proposalData.proposalType,
|
|
48
|
+
proposer: proposalData.proposer,
|
|
49
|
+
amount: {
|
|
50
|
+
raw: proposalData.amount,
|
|
51
|
+
formatted: amountFormatted,
|
|
52
|
+
usd: tokenPrice ? (parseFloat(amountFormatted) * tokenPrice).toString() : undefined,
|
|
53
|
+
},
|
|
54
|
+
recipient: proposalData.recipient,
|
|
55
|
+
description: proposalData.description,
|
|
56
|
+
createdAt: {
|
|
57
|
+
timestamp: proposalData.createdAt,
|
|
58
|
+
date: new Date(Number(proposalData.createdAt) * 1000),
|
|
59
|
+
},
|
|
60
|
+
votingStartsAt: {
|
|
61
|
+
timestamp: proposalData.votingStartsAt,
|
|
62
|
+
date: new Date(Number(proposalData.votingStartsAt) * 1000),
|
|
63
|
+
},
|
|
64
|
+
votingEndsAt: {
|
|
65
|
+
timestamp: proposalData.votingEndsAt,
|
|
66
|
+
date: new Date(Number(proposalData.votingEndsAt) * 1000),
|
|
67
|
+
},
|
|
68
|
+
yesVotes: {
|
|
69
|
+
raw: proposalData.yesVotes,
|
|
70
|
+
formatted: yesVotesFormatted,
|
|
71
|
+
usd: tokenPrice ? (parseFloat(yesVotesFormatted) * tokenPrice).toString() : undefined,
|
|
72
|
+
},
|
|
73
|
+
noVotes: {
|
|
74
|
+
raw: proposalData.noVotes,
|
|
75
|
+
formatted: noVotesFormatted,
|
|
76
|
+
usd: tokenPrice ? (parseFloat(noVotesFormatted) * tokenPrice).toString() : undefined,
|
|
77
|
+
},
|
|
78
|
+
totalBalanceVoted: proposalData.totalBalanceVoted,
|
|
79
|
+
executed: proposalData.executed,
|
|
80
|
+
cycleId: proposalData.cycleId,
|
|
81
|
+
meetsQuorum,
|
|
82
|
+
meetsApproval,
|
|
83
|
+
state,
|
|
84
|
+
voteReceipt: voteReceiptData,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get proposals data from governor contract with enriched data
|
|
89
|
+
* Uses getProposalsForCycle() then single multicall to get all proposal data
|
|
90
|
+
*/
|
|
91
|
+
export async function proposals({ publicClient, governorAddress, cycleId, tokenDecimals = 18, pricing, pageSize = 50, userAddress, }) {
|
|
92
|
+
if (Object.values({ publicClient, governorAddress }).some((value) => !value)) {
|
|
93
|
+
throw new Error("Invalid proposals params");
|
|
94
|
+
}
|
|
95
|
+
// Get current cycle ID if not provided
|
|
96
|
+
const currentCycleId = cycleId ??
|
|
97
|
+
(await publicClient.readContract({
|
|
98
|
+
address: governorAddress,
|
|
99
|
+
abi: LevrGovernor_v1,
|
|
100
|
+
functionName: "currentCycleId",
|
|
101
|
+
}));
|
|
102
|
+
// Get all proposal IDs for the cycle
|
|
103
|
+
const proposalIds = await publicClient.readContract({
|
|
104
|
+
address: governorAddress,
|
|
105
|
+
abi: LevrGovernor_v1,
|
|
106
|
+
functionName: "getProposalsForCycle",
|
|
107
|
+
args: [currentCycleId],
|
|
108
|
+
});
|
|
109
|
+
// Limit to pageSize (most recent first, array is already in reverse order)
|
|
110
|
+
const limitedIds = Array.from(proposalIds).slice(0, pageSize);
|
|
111
|
+
if (limitedIds.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
proposals: [],
|
|
114
|
+
cycleId: currentCycleId,
|
|
115
|
+
winner: 0n,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// Build single multicall using proposalCallData utility (includes vote receipts if userAddress provided)
|
|
119
|
+
const contracts = limitedIds.flatMap((proposalId) => proposalCallData(governorAddress, proposalId, userAddress));
|
|
120
|
+
const results = await publicClient.multicall({ contracts });
|
|
121
|
+
// Parse results using parseProposalData utility
|
|
122
|
+
const parsedProposals = [];
|
|
123
|
+
const contractsPerProposal = userAddress ? 2 : 1; // 2 if including vote receipt, 1 otherwise
|
|
124
|
+
for (let i = 0; i < limitedIds.length; i++) {
|
|
125
|
+
const baseIndex = i * contractsPerProposal;
|
|
126
|
+
const proposalResults = [
|
|
127
|
+
results[baseIndex].result,
|
|
128
|
+
userAddress ? results[baseIndex + 1].result : undefined,
|
|
129
|
+
];
|
|
130
|
+
// Skip if proposal data is invalid
|
|
131
|
+
if (!proposalResults[0]) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
parsedProposals.push(parseProposalData(proposalResults, tokenDecimals, pricing));
|
|
135
|
+
}
|
|
136
|
+
const winner = await publicClient.readContract({
|
|
137
|
+
address: governorAddress,
|
|
138
|
+
abi: LevrGovernor_v1,
|
|
139
|
+
functionName: "getWinner",
|
|
140
|
+
args: [currentCycleId],
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
proposals: parsedProposals,
|
|
144
|
+
cycleId: currentCycleId,
|
|
145
|
+
winner,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export async function proposal(publicClient, governorAddress, proposalId, tokenDecimals, pricing, userAddress) {
|
|
149
|
+
const results = await publicClient.multicall({
|
|
150
|
+
contracts: proposalCallData(governorAddress, proposalId, userAddress),
|
|
151
|
+
});
|
|
152
|
+
// Extract .result from each multicall result
|
|
153
|
+
const extractedResults = results.map((r) => r.result);
|
|
154
|
+
return parseProposalData(extractedResults, tokenDecimals, pricing);
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=proposal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/proposal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Unified quote API for Uniswap V3 and V4
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This module provides quote functionality for both Uniswap V3 and V4 pools.
|
|
6
|
+
* Each version offers two methods:
|
|
7
|
+
* - `read`: Performs an async call to get the quote immediately
|
|
8
|
+
* - `bytecode`: Returns encoded call data for use in multicalls or custom execution
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // V3 Read (immediate result)
|
|
13
|
+
* const v3Quote = await quote.v3.read({
|
|
14
|
+
* publicClient,
|
|
15
|
+
* quoterAddress: '0x...',
|
|
16
|
+
* tokenIn: '0x123...',
|
|
17
|
+
* tokenOut: '0x456...',
|
|
18
|
+
* amountIn: parseUnits('1', 18),
|
|
19
|
+
* fee: 3000,
|
|
20
|
+
* })
|
|
21
|
+
* console.log(`Output: ${formatUnits(v3Quote.amountOut, 6)}`)
|
|
22
|
+
*
|
|
23
|
+
* // V3 Bytecode (for multicall)
|
|
24
|
+
* const v3Bytecode = quote.v3.bytecode({
|
|
25
|
+
* quoterAddress: '0x...',
|
|
26
|
+
* tokenIn: '0x123...',
|
|
27
|
+
* tokenOut: '0x456...',
|
|
28
|
+
* amountIn: parseUnits('1', 18),
|
|
29
|
+
* fee: 3000,
|
|
30
|
+
* })
|
|
31
|
+
* // Use v3Bytecode.address and v3Bytecode.data in multicall
|
|
32
|
+
*
|
|
33
|
+
* // V4 Read (immediate result with hook fees and price impact)
|
|
34
|
+
* const v4Quote = await quote.v4.read({
|
|
35
|
+
* publicClient,
|
|
36
|
+
* poolKey,
|
|
37
|
+
* zeroForOne: true,
|
|
38
|
+
* amountIn: parseEther('1'),
|
|
39
|
+
* pricing,
|
|
40
|
+
* tokenAddress: '0x...',
|
|
41
|
+
* })
|
|
42
|
+
* console.log(`Output: ${formatEther(v4Quote.amountOut)}`)
|
|
43
|
+
* console.log(`Price Impact: ${v4Quote.priceImpactBps}%`)
|
|
44
|
+
* console.log(`Hook Fees:`, v4Quote.hookFees)
|
|
45
|
+
*
|
|
46
|
+
* // V4 Bytecode (for multicall)
|
|
47
|
+
* const v4Bytecode = quote.v4.bytecode({
|
|
48
|
+
* publicClient, // Needed for chain ID
|
|
49
|
+
* poolKey,
|
|
50
|
+
* zeroForOne: true,
|
|
51
|
+
* amountIn: parseEther('1'),
|
|
52
|
+
* })
|
|
53
|
+
* // Use v4Bytecode.address and v4Bytecode.data in multicall
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
// Import implementations
|
|
57
|
+
import { quoteV3Bytecode, quoteV3Read } from "./v3.js";
|
|
58
|
+
import { quoteV4Bytecode, quoteV4Read } from "./v4.js";
|
|
59
|
+
/**
|
|
60
|
+
* @description Unified quote API for Uniswap V3 and V4
|
|
61
|
+
*/
|
|
62
|
+
export const quote = {
|
|
63
|
+
/**
|
|
64
|
+
* Uniswap V3 quote methods
|
|
65
|
+
*/
|
|
66
|
+
v3: {
|
|
67
|
+
/**
|
|
68
|
+
* Get a V3 quote by reading from the quoter contract
|
|
69
|
+
* @param params Quote parameters
|
|
70
|
+
* @returns Quote result with output amount
|
|
71
|
+
*/
|
|
72
|
+
read: quoteV3Read,
|
|
73
|
+
/**
|
|
74
|
+
* Get encoded bytecode for a V3 quote (for multicalls)
|
|
75
|
+
* @param params Quote parameters
|
|
76
|
+
* @returns Contract address and encoded call data
|
|
77
|
+
*/
|
|
78
|
+
bytecode: quoteV3Bytecode,
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* Uniswap V4 quote methods
|
|
82
|
+
*/
|
|
83
|
+
v4: {
|
|
84
|
+
/**
|
|
85
|
+
* Get a V4 quote by reading from the quoter contract
|
|
86
|
+
* @param params Quote parameters
|
|
87
|
+
* @returns Quote result with output amount, gas estimate, price impact, and hook fees
|
|
88
|
+
*/
|
|
89
|
+
read: quoteV4Read,
|
|
90
|
+
/**
|
|
91
|
+
* Get encoded bytecode for a V4 quote (for multicalls)
|
|
92
|
+
* @param params Quote parameters
|
|
93
|
+
* @returns Contract address and encoded call data
|
|
94
|
+
*/
|
|
95
|
+
bytecode: quoteV4Bytecode,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/quote/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAMH,yBAAyB;AACzB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA;AAEnD;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB;;OAEG;IACH,EAAE,EAAE;QACF;;;;WAIG;QACH,IAAI,EAAE,WAAW;QACjB;;;;WAIG;QACH,QAAQ,EAAE,eAAe;KAC1B;IACD;;OAEG;IACH,EAAE,EAAE;QACF;;;;WAIG;QACH,IAAI,EAAE,WAAW;QACjB;;;;WAIG;QACH,QAAQ,EAAE,eAAe;KAC1B;CACF,CAAA"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { encodeFunctionData } from "viem";
|
|
2
|
+
import { V3QuoterV2 } from "../abis/index.js";
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// V3 Implementation
|
|
5
|
+
// ============================================================================
|
|
6
|
+
/**
|
|
7
|
+
* @description Quote a swap on Uniswap V3 by reading from the quoter contract
|
|
8
|
+
* @param params Parameters for V3 quote
|
|
9
|
+
* @returns Output amount and fee tier
|
|
10
|
+
*/
|
|
11
|
+
export const quoteV3Read = async (params) => {
|
|
12
|
+
if (!params.publicClient) {
|
|
13
|
+
throw new Error("publicClient is required for read method");
|
|
14
|
+
}
|
|
15
|
+
const { publicClient, quoterAddress, tokenIn, tokenOut, amountIn, fee, sqrtPriceLimitX96 = 0n, } = params;
|
|
16
|
+
const result = await publicClient.simulateContract({
|
|
17
|
+
address: quoterAddress,
|
|
18
|
+
abi: V3QuoterV2,
|
|
19
|
+
functionName: "quoteExactInputSingle",
|
|
20
|
+
args: [
|
|
21
|
+
{
|
|
22
|
+
tokenIn,
|
|
23
|
+
tokenOut,
|
|
24
|
+
amountIn,
|
|
25
|
+
fee,
|
|
26
|
+
sqrtPriceLimitX96,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const [amountOut] = result.result;
|
|
31
|
+
return {
|
|
32
|
+
amountOut,
|
|
33
|
+
fee,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* @description Get bytecode for a V3 quote that can be used in multicalls
|
|
38
|
+
* @param params Parameters for V3 quote
|
|
39
|
+
* @returns Contract address, encoded call data, and ABI
|
|
40
|
+
*/
|
|
41
|
+
export const quoteV3Bytecode = (params) => {
|
|
42
|
+
const { quoterAddress, tokenIn, tokenOut, amountIn, fee, sqrtPriceLimitX96 = 0n } = params;
|
|
43
|
+
const data = encodeFunctionData({
|
|
44
|
+
abi: V3QuoterV2,
|
|
45
|
+
functionName: "quoteExactInputSingle",
|
|
46
|
+
args: [
|
|
47
|
+
{
|
|
48
|
+
tokenIn,
|
|
49
|
+
tokenOut,
|
|
50
|
+
amountIn,
|
|
51
|
+
fee,
|
|
52
|
+
sqrtPriceLimitX96,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
address: quoterAddress,
|
|
58
|
+
data,
|
|
59
|
+
abi: V3QuoterV2,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=v3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/quote/v3.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAwEpC,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,MAAqB,EAAkC,EAAE;IACzF,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC7D,CAAC;IAED,MAAM,EACJ,YAAY,EACZ,aAAa,EACb,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,iBAAiB,GAAG,EAAE,GACvB,GAAG,MAAM,CAAA;IAEV,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,gBAAgB,CAAC;QACjD,OAAO,EAAE,aAAa;QACtB,GAAG,EAAE,UAAU;QACf,YAAY,EAAE,uBAAuB;QACrC,IAAI,EAAE;YACJ;gBACE,OAAO;gBACP,QAAQ;gBACR,QAAQ;gBACR,GAAG;gBACH,iBAAiB;aAClB;SACF;KACF,CAAC,CAAA;IAEF,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IAEjC,OAAO;QACL,SAAS;QACT,GAAG;KACJ,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAqB,EAA6B,EAAE;IAClF,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,iBAAiB,GAAG,EAAE,EAAE,GAAG,MAAM,CAAA;IAE1F,MAAM,IAAI,GAAG,kBAAkB,CAAC;QAC9B,GAAG,EAAE,UAAU;QACf,YAAY,EAAE,uBAAuB;QACrC,IAAI,EAAE;YACJ;gBACE,OAAO;gBACP,QAAQ;gBACR,QAAQ;gBACR,GAAG;gBACH,iBAAiB;aAClB;SACF;KACF,CAAC,CAAA;IAEF,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,IAAI;QACJ,GAAG,EAAE,UAAU;KAChB,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { encodeAbiParameters, encodeFunctionData, formatUnits, keccak256 } from "viem";
|
|
2
|
+
import { IClankerHookDynamicFee, IClankerHookStaticFee, V4Quoter } from "../abis/index.js";
|
|
3
|
+
import { UNISWAP_V4_QUOTER } from "../constants.js";
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// V4 Helper Functions
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* @description Try to get static fees from a Clanker hook using multicall
|
|
9
|
+
*/
|
|
10
|
+
const tryGetStaticFees = async (publicClient, hookAddress, poolId) => {
|
|
11
|
+
try {
|
|
12
|
+
const results = await publicClient.multicall({
|
|
13
|
+
contracts: [
|
|
14
|
+
{
|
|
15
|
+
address: hookAddress,
|
|
16
|
+
abi: IClankerHookStaticFee,
|
|
17
|
+
functionName: "clankerFee",
|
|
18
|
+
args: [poolId],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
address: hookAddress,
|
|
22
|
+
abi: IClankerHookStaticFee,
|
|
23
|
+
functionName: "pairedFee",
|
|
24
|
+
args: [poolId],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
if (results[0].status === "success" && results[1].status === "success") {
|
|
29
|
+
return {
|
|
30
|
+
clankerFee: Number(results[0].result),
|
|
31
|
+
pairedFee: Number(results[1].result),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* @description Try to get dynamic fee configuration from a Clanker hook
|
|
42
|
+
*/
|
|
43
|
+
const tryGetDynamicFees = async (publicClient, hookAddress, poolId) => {
|
|
44
|
+
try {
|
|
45
|
+
const config = await publicClient.readContract({
|
|
46
|
+
address: hookAddress,
|
|
47
|
+
abi: IClankerHookDynamicFee,
|
|
48
|
+
functionName: "poolConfigVars",
|
|
49
|
+
args: [poolId],
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
baseFee: Number(config.baseFee),
|
|
53
|
+
maxLpFee: Number(config.maxLpFee),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* @description Get hook fee information from Clanker hooks
|
|
62
|
+
*/
|
|
63
|
+
const getHookFees = async (publicClient, poolKey) => {
|
|
64
|
+
// Generate pool ID for hook queries (keccak256 of abi.encode(PoolKey))
|
|
65
|
+
const poolId = keccak256(encodeAbiParameters([
|
|
66
|
+
{
|
|
67
|
+
type: "tuple",
|
|
68
|
+
components: [
|
|
69
|
+
{ name: "currency0", type: "address" },
|
|
70
|
+
{ name: "currency1", type: "address" },
|
|
71
|
+
{ name: "fee", type: "uint24" },
|
|
72
|
+
{ name: "tickSpacing", type: "int24" },
|
|
73
|
+
{ name: "hooks", type: "address" },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
], [poolKey]));
|
|
77
|
+
// Try static fees first
|
|
78
|
+
const staticFees = await tryGetStaticFees(publicClient, poolKey.hooks, poolId);
|
|
79
|
+
if (staticFees) {
|
|
80
|
+
return {
|
|
81
|
+
type: "static",
|
|
82
|
+
clankerFee: staticFees.clankerFee,
|
|
83
|
+
pairedFee: staticFees.pairedFee,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Try dynamic fees
|
|
87
|
+
const dynamicFees = await tryGetDynamicFees(publicClient, poolKey.hooks, poolId);
|
|
88
|
+
if (dynamicFees) {
|
|
89
|
+
return {
|
|
90
|
+
type: "dynamic",
|
|
91
|
+
baseFee: dynamicFees.baseFee,
|
|
92
|
+
maxLpFee: dynamicFees.maxLpFee,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* @description Calculate price impact using USD pricing
|
|
99
|
+
* Compares the execution price to the market spot price
|
|
100
|
+
*/
|
|
101
|
+
const calculatePriceImpact = (amountIn, amountOut, currency0, currency1, currency0Decimals, currency1Decimals, tokenAddress, pricing, zeroForOne) => {
|
|
102
|
+
try {
|
|
103
|
+
// Determine which currency is the token and which is WETH
|
|
104
|
+
const currency0IsToken = currency0.toLowerCase() === tokenAddress.toLowerCase();
|
|
105
|
+
// Format amounts to decimal strings
|
|
106
|
+
const amountInFormatted = parseFloat(formatUnits(amountIn, zeroForOne ? currency0Decimals : currency1Decimals));
|
|
107
|
+
const amountOutFormatted = parseFloat(formatUnits(amountOut, zeroForOne ? currency1Decimals : currency0Decimals));
|
|
108
|
+
if (amountInFormatted === 0 || amountOutFormatted === 0)
|
|
109
|
+
return undefined;
|
|
110
|
+
// Get market spot prices
|
|
111
|
+
const wethPrice = parseFloat(pricing.wethUsd);
|
|
112
|
+
const tokenPrice = parseFloat(pricing.tokenUsd);
|
|
113
|
+
if (tokenPrice === 0 || wethPrice === 0)
|
|
114
|
+
return undefined;
|
|
115
|
+
// Calculate execution rate (how many output per input)
|
|
116
|
+
const executionRate = amountOutFormatted / amountInFormatted;
|
|
117
|
+
// Determine swap direction and calculate market rate
|
|
118
|
+
const inputIsToken = zeroForOne ? currency0IsToken : !currency0IsToken;
|
|
119
|
+
// Calculate market spot rate (output per input at market prices)
|
|
120
|
+
const marketRate = inputIsToken
|
|
121
|
+
? tokenPrice / wethPrice // Token → WETH
|
|
122
|
+
: wethPrice / tokenPrice; // WETH → Token
|
|
123
|
+
// Getting better or equal rate than market - minimal impact
|
|
124
|
+
if (executionRate >= marketRate) {
|
|
125
|
+
return 0.1;
|
|
126
|
+
}
|
|
127
|
+
// Getting worse rate than market - calculate actual slippage
|
|
128
|
+
const impact = (1 - executionRate / marketRate) * 100;
|
|
129
|
+
return impact;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.warn("Price impact calculation failed:", error);
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// V4 Implementation
|
|
138
|
+
// ============================================================================
|
|
139
|
+
/**
|
|
140
|
+
* @description Quote a swap on Uniswap V4 by reading from the quoter contract
|
|
141
|
+
* @param params Quote parameters including pool key and amount
|
|
142
|
+
* @returns Quote result with output amount, gas estimate, price impact, and hook fees
|
|
143
|
+
*/
|
|
144
|
+
export const quoteV4Read = async (params) => {
|
|
145
|
+
if (!params.publicClient) {
|
|
146
|
+
throw new Error("publicClient is required for read method");
|
|
147
|
+
}
|
|
148
|
+
const { publicClient, poolKey, zeroForOne, amountIn, hookData = "0x", pricing, currency0Decimals = 18, currency1Decimals = 18, tokenAddress, } = params;
|
|
149
|
+
const chainId = publicClient.chain?.id;
|
|
150
|
+
if (!chainId)
|
|
151
|
+
throw new Error("Chain ID not found on public client");
|
|
152
|
+
const quoterAddress = UNISWAP_V4_QUOTER(chainId);
|
|
153
|
+
if (!quoterAddress)
|
|
154
|
+
throw new Error(`V4 Quoter address not found for chain ID ${chainId}`);
|
|
155
|
+
// Fetch hook fees and quote in parallel
|
|
156
|
+
const [{ result }, hookFees] = await Promise.all([
|
|
157
|
+
publicClient.simulateContract({
|
|
158
|
+
address: quoterAddress,
|
|
159
|
+
abi: V4Quoter,
|
|
160
|
+
functionName: "quoteExactInputSingle",
|
|
161
|
+
args: [
|
|
162
|
+
{
|
|
163
|
+
poolKey: {
|
|
164
|
+
currency0: poolKey.currency0,
|
|
165
|
+
currency1: poolKey.currency1,
|
|
166
|
+
fee: poolKey.fee,
|
|
167
|
+
tickSpacing: poolKey.tickSpacing,
|
|
168
|
+
hooks: poolKey.hooks,
|
|
169
|
+
},
|
|
170
|
+
zeroForOne,
|
|
171
|
+
exactAmount: amountIn,
|
|
172
|
+
hookData,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
}),
|
|
176
|
+
getHookFees(publicClient, poolKey),
|
|
177
|
+
]);
|
|
178
|
+
// The quoter returns [amountOut: bigint, gasEstimate: bigint]
|
|
179
|
+
const [amountOut, gasEstimate] = result;
|
|
180
|
+
// Calculate price impact if pricing and token address are available
|
|
181
|
+
const priceImpactBps = pricing && tokenAddress
|
|
182
|
+
? calculatePriceImpact(amountIn, amountOut, poolKey.currency0, poolKey.currency1, currency0Decimals, currency1Decimals, tokenAddress, pricing, zeroForOne)
|
|
183
|
+
: undefined;
|
|
184
|
+
return {
|
|
185
|
+
amountOut,
|
|
186
|
+
gasEstimate,
|
|
187
|
+
priceImpactBps,
|
|
188
|
+
hookFees,
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* @description Get bytecode for a V4 quote that can be used in multicalls
|
|
193
|
+
* @param params Quote parameters including pool key and amount
|
|
194
|
+
* @returns Contract address, encoded call data, and ABI
|
|
195
|
+
*/
|
|
196
|
+
export const quoteV4Bytecode = (params) => {
|
|
197
|
+
const { poolKey, zeroForOne, amountIn, hookData = "0x", publicClient } = params;
|
|
198
|
+
const chainId = publicClient?.chain?.id;
|
|
199
|
+
if (!chainId)
|
|
200
|
+
throw new Error("Chain ID required for bytecode generation");
|
|
201
|
+
const quoterAddress = UNISWAP_V4_QUOTER(chainId);
|
|
202
|
+
if (!quoterAddress)
|
|
203
|
+
throw new Error(`V4 Quoter address not found for chain ID ${chainId}`);
|
|
204
|
+
const data = encodeFunctionData({
|
|
205
|
+
abi: V4Quoter,
|
|
206
|
+
functionName: "quoteExactInputSingle",
|
|
207
|
+
args: [
|
|
208
|
+
{
|
|
209
|
+
poolKey: {
|
|
210
|
+
currency0: poolKey.currency0,
|
|
211
|
+
currency1: poolKey.currency1,
|
|
212
|
+
fee: poolKey.fee,
|
|
213
|
+
tickSpacing: poolKey.tickSpacing,
|
|
214
|
+
hooks: poolKey.hooks,
|
|
215
|
+
},
|
|
216
|
+
zeroForOne,
|
|
217
|
+
exactAmount: amountIn,
|
|
218
|
+
hookData,
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
});
|
|
222
|
+
return {
|
|
223
|
+
address: quoterAddress,
|
|
224
|
+
data,
|
|
225
|
+
abi: V4Quoter,
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
//# sourceMappingURL=v4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v4.js","sourceRoot":"","sources":["../../../src/quote/v4.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAEtF,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAyFhD,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,gBAAgB,GAAG,KAAK,EAC5B,YAA0B,EAC1B,WAA0B,EAC1B,MAAqB,EAC2C,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC;YAC3C,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,WAAW;oBACpB,GAAG,EAAE,qBAAqB;oBAC1B,YAAY,EAAE,YAAY;oBAC1B,IAAI,EAAE,CAAC,MAAM,CAAC;iBACf;gBACD;oBACE,OAAO,EAAE,WAAW;oBACpB,GAAG,EAAE,qBAAqB;oBAC1B,YAAY,EAAE,WAAW;oBACzB,IAAI,EAAE,CAAC,MAAM,CAAC;iBACf;aACF;SACF,CAAC,CAAA;QAEF,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACvE,OAAO;gBACL,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gBACrC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;aACrC,CAAA;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAC7B,YAA0B,EAC1B,WAA0B,EAC1B,MAAqB,EACuC,EAAE;IAC9D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC;YAC7C,OAAO,EAAE,WAAW;YACpB,GAAG,EAAE,sBAAsB;YAC3B,YAAY,EAAE,gBAAgB;YAC9B,IAAI,EAAE,CAAC,MAAM,CAAC;SACf,CAAC,CAAA;QACF,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;YAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;SAClC,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,WAAW,GAAG,KAAK,EACvB,YAA0B,EAC1B,OAAgB,EAC4B,EAAE;IAC9C,uEAAuE;IACvE,MAAM,MAAM,GAAG,SAAS,CACtB,mBAAmB,CACjB;QACE;YACE,IAAI,EAAE,OAAO;YACb,UAAU,EAAE;gBACV,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE;gBACtC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE;gBACtC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC/B,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE;gBACtC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;aACnC;SACF;KACF,EACD,CAAC,OAAO,CAAC,CACV,CACF,CAAA;IAED,wBAAwB;IACxB,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC9E,IAAI,UAAU,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,UAAU,CAAC,UAAU;YACjC,SAAS,EAAE,UAAU,CAAC,SAAS;SAChC,CAAA;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAChF,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,QAAQ,EAAE,WAAW,CAAC,QAAQ;SAC/B,CAAA;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,oBAAoB,GAAG,CAC3B,QAAgB,EAChB,SAAiB,EACjB,SAAwB,EACxB,SAAwB,EACxB,iBAAyB,EACzB,iBAAyB,EACzB,YAA2B,EAC3B,OAAsB,EACtB,UAAmB,EACC,EAAE;IACtB,IAAI,CAAC;QACH,0DAA0D;QAC1D,MAAM,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAAA;QAE/E,oCAAoC;QACpC,MAAM,iBAAiB,GAAG,UAAU,CAClC,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAC1E,CAAA;QACD,MAAM,kBAAkB,GAAG,UAAU,CACnC,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAC3E,CAAA;QAED,IAAI,iBAAiB,KAAK,CAAC,IAAI,kBAAkB,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAEzE,yBAAyB;QACzB,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAE/C,IAAI,UAAU,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAEzD,uDAAuD;QACvD,MAAM,aAAa,GAAG,kBAAkB,GAAG,iBAAiB,CAAA;QAE5D,qDAAqD;QACrD,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAA;QAEtE,iEAAiE;QACjE,MAAM,UAAU,GAAG,YAAY;YAC7B,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,eAAe;YACxC,CAAC,CAAC,SAAS,GAAG,UAAU,CAAA,CAAC,eAAe;QAE1C,4DAA4D;QAC5D,IAAI,aAAa,IAAI,UAAU,EAAE,CAAC;YAChC,OAAO,GAAG,CAAA;QACZ,CAAC;QAED,6DAA6D;QAC7D,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,aAAa,GAAG,UAAU,CAAC,GAAG,GAAG,CAAA;QAErD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAA;QACvD,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,MAAqB,EAAkC,EAAE;IACzF,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC7D,CAAC;IAED,MAAM,EACJ,YAAY,EACZ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,QAAQ,GAAG,IAAI,EACf,OAAO,EACP,iBAAiB,GAAG,EAAE,EACtB,iBAAiB,GAAG,EAAE,EACtB,YAAY,GACb,GAAG,MAAM,CAAA;IAEV,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,EAAE,CAAA;IACtC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IAEpE,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,EAAE,CAAC,CAAA;IAE1F,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/C,YAAY,CAAC,gBAAgB,CAAC;YAC5B,OAAO,EAAE,aAAa;YACtB,GAAG,EAAE,QAAQ;YACb,YAAY,EAAE,uBAAuB;YACrC,IAAI,EAAE;gBACJ;oBACE,OAAO,EAAE;wBACP,SAAS,EAAE,OAAO,CAAC,SAAS;wBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;wBAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;wBAChB,WAAW,EAAE,OAAO,CAAC,WAAW;wBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;qBACrB;oBACD,UAAU;oBACV,WAAW,EAAE,QAAQ;oBACrB,QAAQ;iBACT;aACF;SACF,CAAC;QACF,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC;KACnC,CAAC,CAAA;IAEF,8DAA8D;IAC9D,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,MAAM,CAAA;IAEvC,oEAAoE;IACpE,MAAM,cAAc,GAClB,OAAO,IAAI,YAAY;QACrB,CAAC,CAAC,oBAAoB,CAClB,QAAQ,EACR,SAAS,EACT,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,SAAS,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACP,UAAU,CACX;QACH,CAAC,CAAC,SAAS,CAAA;IAEf,OAAO;QACL,SAAS;QACT,WAAW;QACX,cAAc;QACd,QAAQ;KACT,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAqB,EAA6B,EAAE;IAClF,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,CAAA;IAE/E,MAAM,OAAO,GAAG,YAAY,EAAE,KAAK,EAAE,EAAE,CAAA;IACvC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;IAE1E,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,EAAE,CAAC,CAAA;IAE1F,MAAM,IAAI,GAAG,kBAAkB,CAAC;QAC9B,GAAG,EAAE,QAAQ;QACb,YAAY,EAAE,uBAAuB;QACrC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE;oBACP,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;oBAChB,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;iBACrB;gBACD,UAAU;gBACV,WAAW,EAAE,QAAQ;gBACrB,QAAQ;aACT;SACF;KACF,CAAC,CAAA;IAEF,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,IAAI;QACJ,GAAG,EAAE,QAAQ;KACd,CAAA;AACH,CAAC,CAAA"}
|