Skip to content

Commit a238273

Browse files
committed
feat: add new abi encoder widget
1 parent ce80736 commit a238273

File tree

8 files changed

+320
-16
lines changed

8 files changed

+320
-16
lines changed

docs/developers/ypools/yeth/adding-assets.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ You will need access to the necessary governance contracts and a sufficient voti
1212

1313
### Step 1: Check Inclusion Vote Status
1414

15-
Since the adoption of [this](https://snapshot.org/#/ylsd.eth/proposal/0x139698bed7752b80a16bb6d2fc0d9e8c82b622916ded2f064022be3c46ec9bb4) proposal, inclusion voting is off by default.
15+
Since the adoption of [this](https://snapshot.org/#/ylsd.eth/proposal/0x139698bed7752b80a16bb6d2fc0d9e8c82b622916ded2f064022be3c46ec9bb4) proposal, inclusion voting is off by default and needs to be enabled for the next epoch
1616

1717
### Step 2: Enable One-Off Inclusion Vote
1818

docs/developers/ypools/yeth/create-gov-proposal.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ You will need access to the necessary governance contracts and a sufficient voti
2222
1. Generate a script by calling the `script(_to, _data)` view function on the [Executor contract](https://etherscan.io/address/0x71258Ee726644f1D52d6A9F5E11C21d1E38c2bF1#readContract#F1).
2323

2424
In this case, set the `_to(address)` field to the [InclusionVote contract](../../addresses/ypools-contracts.md#yeth-contract-addresses) `0x6bc0878939669339e82dbFa13d260c89230f2c31` and use ABI-encoded data for the function set_enable_epoch(next_epoch) as the calldata (_data).
25+
26+
<AbiEncodingWidget defaultAbi='yPoolsInclusionVoteABI' defaultFunction='set_enable_epoch' functionArg='20'/>
27+
2528
2. To enable multiple calls in a single proposal, concatenate the scripts together.
2629

2730
## Step 2: Submit a Governance Proposal

src/components/AbiEncoder.tsx

Lines changed: 108 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,114 @@
1-
import React from 'react'
1+
import React, { useState, useMemo } from 'react'
2+
import * as AllABIs from '../ethereum/ABIs'
23
import { encodeFunctionData } from 'viem'
3-
import { yPoolsInclusionVoteABI } from '../ethereum/ABIs'
4+
import {
5+
Card,
6+
CardHeader,
7+
CardTitle,
8+
CardContent,
9+
CardFooter,
10+
} from './shadcn/card/card'
11+
import {
12+
Select,
13+
SelectTrigger,
14+
SelectValue,
15+
SelectContent,
16+
SelectItem,
17+
} from './shadcn/select/select'
18+
import Input from './shadcn/input/input'
19+
import { Button } from './shadcn/button/button'
420

5-
const AbiEncoder = (epoch: number) => {
6-
const data = encodeFunctionData({
7-
abi: yPoolsInclusionVoteABI,
8-
functionName: 'set_enable_epoch',
9-
args: [BigInt(epoch)],
10-
})
21+
interface AbiEncodingWidgetProps {
22+
defaultAbi?: string
23+
defaultFunction?: string
24+
}
25+
26+
export function AbiEncodingWidget({
27+
defaultAbi = 'yPoolsInclusionVoteABI',
28+
defaultFunction = 'set_enable_epoch',
29+
}: AbiEncodingWidgetProps) {
30+
const [selectedAbiKey, setSelectedAbiKey] = useState(defaultAbi)
31+
const [selectedFunction, setSelectedFunction] = useState(defaultFunction)
32+
const [argValue, setArgValue] = useState<string>('')
33+
const [encodedData, setEncodedData] = useState('')
34+
35+
const allAbiKeys = Object.keys(AllABIs).filter((key) =>
36+
Array.isArray((AllABIs as any)[key])
37+
)
38+
const functions = useMemo(() => {
39+
const abi = (AllABIs as any)[selectedAbiKey] || []
40+
return abi
41+
.filter((entry: any) => entry.type === 'function')
42+
.map((entry: any) => entry.name)
43+
}, [selectedAbiKey])
44+
45+
const encodeData = () => {
46+
try {
47+
const abi = (AllABIs as any)[selectedAbiKey]
48+
const data = encodeFunctionData({
49+
abi,
50+
functionName: selectedFunction,
51+
// For simplicity, assume one argument that requires a bigint
52+
args: [BigInt(argValue || '0')],
53+
})
54+
setEncodedData(data)
55+
} catch (err) {
56+
setEncodedData('Error encoding data')
57+
}
58+
}
1159

12-
console.log(data)
1360
return (
14-
<div>
15-
<h3>AbiEncoder</h3>
16-
</div>
61+
<Card>
62+
<CardHeader>
63+
<CardTitle>ABI Encoder</CardTitle>
64+
</CardHeader>
65+
<CardContent>
66+
<Select
67+
value={selectedAbiKey}
68+
onValueChange={(val) => setSelectedAbiKey(val)}
69+
>
70+
<SelectTrigger>
71+
<SelectValue placeholder="Select ABI" />
72+
</SelectTrigger>
73+
<SelectContent>
74+
{allAbiKeys.map((key) => (
75+
<SelectItem key={key} value={key}>
76+
{key}
77+
</SelectItem>
78+
))}
79+
</SelectContent>
80+
</Select>
81+
<Select
82+
value={selectedFunction}
83+
onValueChange={(val) => setSelectedFunction(val)}
84+
>
85+
<SelectTrigger>
86+
<SelectValue placeholder="Select function" />
87+
</SelectTrigger>
88+
<SelectContent>
89+
{functions.map((fn) => (
90+
<SelectItem key={fn} value={fn}>
91+
{fn}
92+
</SelectItem>
93+
))}
94+
</SelectContent>
95+
</Select>
96+
<Input
97+
type="text"
98+
placeholder="Function argument"
99+
value={argValue}
100+
onChange={(e) => setArgValue(e.target.value)}
101+
/>
102+
</CardContent>
103+
<CardFooter>
104+
<Button onClick={encodeData}>Encode</Button>
105+
</CardFooter>
106+
{encodedData && (
107+
<div style={{ marginTop: '1rem', wordBreak: 'break-all' }}>
108+
{encodedData}
109+
</div>
110+
)}
111+
</Card>
17112
)
18113
}
19-
20-
export default AbiEncoder
114+
export default AbiEncodingWidget
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import React, { useState, useMemo } from 'react'
2+
import * as AllABIs from '../ethereum/ABIs'
3+
import { encodeFunctionData } from 'viem'
4+
import {
5+
Card,
6+
CardHeader,
7+
CardTitle,
8+
CardContent,
9+
CardFooter,
10+
} from './shadcn/card/card'
11+
import {
12+
Select,
13+
SelectTrigger,
14+
SelectValue,
15+
SelectContent,
16+
SelectItem,
17+
} from './shadcn/select/select'
18+
import Input from './shadcn/input/input'
19+
import { Button } from './shadcn/button/button'
20+
import styles from '../css/widgets.module.css'
21+
import { CopyIcon } from 'lucide-react'
22+
23+
interface AbiEncodingWidgetProps {
24+
defaultAbi?: string
25+
defaultFunction?: string
26+
widgetTitle?: string | undefined
27+
functionArg?: string
28+
}
29+
30+
export function AbiEncodingWidget({
31+
defaultAbi = 'yPoolsInclusionVoteABI',
32+
defaultFunction = 'set_enable_epoch',
33+
widgetTitle = undefined,
34+
functionArg = '',
35+
}: AbiEncodingWidgetProps) {
36+
const [selectedAbiKey, setSelectedAbiKey] = useState(defaultAbi)
37+
const [selectedFunction, setSelectedFunction] = useState(defaultFunction)
38+
const [argValue, setArgValue] = useState<string>(functionArg)
39+
const [encodedData, setEncodedData] = useState('')
40+
41+
const allAbiKeys = Object.keys(AllABIs).filter((key) =>
42+
Array.isArray((AllABIs as any)[key])
43+
)
44+
const functions = useMemo(() => {
45+
const abi = (AllABIs as any)[selectedAbiKey] || []
46+
return abi
47+
.filter((entry: any) => entry.type === 'function')
48+
.map((entry: any) => entry.name)
49+
}, [selectedAbiKey])
50+
51+
const encodeData = () => {
52+
try {
53+
const abi = (AllABIs as any)[selectedAbiKey]
54+
const data = encodeFunctionData({
55+
abi,
56+
functionName: selectedFunction,
57+
// For simplicity, assume one argument that requires a bigint
58+
args: [BigInt(argValue || '0')],
59+
})
60+
setEncodedData(data)
61+
} catch (err) {
62+
setEncodedData('Error encoding data')
63+
}
64+
}
65+
66+
const copyToClipboard = () => {
67+
navigator.clipboard.writeText(encodedData)
68+
}
69+
70+
return (
71+
<>
72+
<Card style={{ padding: '1rem' }}>
73+
{widgetTitle && (
74+
<CardHeader className={styles.CardHeader}>
75+
<CardTitle>{widgetTitle}</CardTitle>
76+
</CardHeader>
77+
)}
78+
<CardContent
79+
style={{ display: 'flex', flexDirection: 'row', gap: '1rem' }}
80+
>
81+
<Select
82+
value={selectedAbiKey}
83+
onValueChange={(val) => setSelectedAbiKey(val)}
84+
>
85+
<SelectTrigger>
86+
<SelectValue placeholder="Select ABI" />
87+
</SelectTrigger>
88+
<SelectContent>
89+
{allAbiKeys.map((key) => (
90+
<SelectItem key={key} value={key}>
91+
{key}
92+
</SelectItem>
93+
))}
94+
</SelectContent>
95+
</Select>
96+
<Select
97+
value={selectedFunction}
98+
onValueChange={(val) => setSelectedFunction(val)}
99+
>
100+
<SelectTrigger>
101+
<SelectValue placeholder="Select function" />
102+
</SelectTrigger>
103+
<SelectContent>
104+
{functions.map((fn) => (
105+
<SelectItem key={fn} value={fn}>
106+
{fn}
107+
</SelectItem>
108+
))}
109+
</SelectContent>
110+
</Select>
111+
<Input
112+
style={{ textAlign: 'right' }}
113+
type="text"
114+
placeholder={functionArg ? functionArg : 'Function argument'}
115+
value={argValue}
116+
onChange={(e) => setArgValue(e.target.value)}
117+
/>
118+
</CardContent>
119+
<CardFooter className={styles.CardFooter}>
120+
<Button
121+
onClick={encodeData}
122+
style={{ width: '100%', maxWidth: 'none' }}
123+
disabled={!selectedAbiKey || !selectedFunction || !argValue}
124+
>
125+
Encode
126+
</Button>
127+
{encodedData && (
128+
<>
129+
<strong>Result:</strong>
130+
<Card
131+
className={styles.encodedDataCard}
132+
onClick={copyToClipboard}
133+
>
134+
<CardContent style={{ wordBreak: 'break-all' }}>
135+
{encodedData}
136+
</CardContent>
137+
<div className={styles.copyIcon}>
138+
<CopyIcon />
139+
</div>
140+
</Card>
141+
</>
142+
)}
143+
</CardFooter>
144+
</Card>
145+
</>
146+
)
147+
}
148+
149+
export default AbiEncodingWidget

src/components/veYFI-calculator.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import {
3131
ResponsiveContainer,
3232
} from 'recharts'
3333
import styles from '../css/veYFI-calc.module.css'
34-
import Label from './shadcn/label/label'
3534
import { Button } from './shadcn/button/button'
3635
import VeYFILockCalculator from './VeYFILockCalculator' // Import the new component
3736

src/css/widgets.module.css

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.CardHeader {
2+
padding: 0;
3+
padding-bottom: 1rem;
4+
}
5+
6+
.CardContent {
7+
display: flex;
8+
flex-direction: column;
9+
position: relative;
10+
padding: 0 1.5rem 0 1.5rem;
11+
gap: 1rem;
12+
}
13+
14+
.inputElements {
15+
display: flex;
16+
flex-direction: row;
17+
gap: 1rem;
18+
width: 100%;
19+
}
20+
21+
.CardFooter {
22+
display: flex;
23+
flex-direction: column;
24+
gap: 1rem;
25+
align-items: left;
26+
padding: 0;
27+
justify-content: space-between;
28+
padding-top: 1rem;
29+
}
30+
31+
.encodedDataCard {
32+
margin-top: -1rem;
33+
padding: 0.5rem;
34+
cursor: pointer;
35+
position: relative;
36+
}
37+
38+
.copyIcon {
39+
position: absolute;
40+
top: 10px;
41+
right: 10px;
42+
opacity: 0;
43+
transition: opacity 0.3s;
44+
}
45+
46+
.encodedDataCard:hover .copyIcon {
47+
opacity: 0.5;
48+
}

src/theme/MDXComponents.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ContractAddress from '@site/src/components/StaticContractAddress'
66
import AddressCheck from '@site/src/components/AddressCheck'
77
import VeYFICalculator from '../components/veYFI-calculator'
88
import GovDataYPools from '@site/src/components/GovDataYPools'
9+
import AbiEncodingWidget from '@site/src/components/AbiEncodingWidget'
910

1011
/**
1112
* Manually add the custom components to the list of MDXComponents that docusaurus uses
@@ -18,4 +19,5 @@ export default {
1819
AddressCheck,
1920
VeYFICalculator,
2021
GovDataYPools,
22+
AbiEncodingWidget,
2123
}

static/img/icons/copy-icon.svg

Lines changed: 9 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)