Skip to main content

hotshot_contract_adapter/
sol_types.rs

1//! Solidity types for interacting with contracts
2//! Re-export types that are used, sometimes renamed to avoid collision.
3//!
4//! TODO: (alex) Due to <https://github.com/foundry-rs/foundry/issues/10153>,
5//! try to re-export the same type from the "youngest" child contract since that is the contract whose functions are being called,
6//! thus from whom the rust bindings are expected.
7//! E.g. Both PlonkVerifier and LightClient, and LightClientV2 depends on BN254. The inheritance relationship is:
8//!   BN254 <- PlonkVerifier <- LIghtClient <- LightClientV2
9//! Most of the time, we interact with PlonkVerifier's function via LightClientV2, thus import BN254.G1Point from `bindings::plonkverifierv2`.
10//! When we need to directly interact with PlonkVerifier's method, implement stupid plain `From<lc2::BN254::G1Point> for pv::BN254::G1Point`.
11//! If you are lazy, you can even use unsafe memory transmute since they are literally the same representation, duplicated in different modules,
12//! thus treated by the rust type systems as distinct types.
13//!
14//! Another usage is in the differential testing in Solidity tests. In those cases, the actual types don't matter, since they will all `abi_encode()`
15//! into the exact same bytes before being communicated over to contract via FFI. Thus using any one of them is fine.
16
17use alloy::sol;
18
19/// # What to re-export, what to hide?
20/// - export contract struct itself, but try to avoid export instance type (instead, use ::new() to get a handle)
21/// - avoid exporting `xxCall` and `xxReturn` types, they usually can be converted/transmuted from existing struct
22/// - Event types should be exported
23/// - structs should be exported and renamed with `xxSol` suffix to avoid confusion with other rust types
24///   - see module doc for more explanation on types duplication issue in alloy
25pub use crate::bindings::{
26    access_control_upgradeable::AccessControlUpgradeable,
27    erc1967_proxy::ERC1967Proxy,
28    esp_token::EspToken,
29    esp_token_v2::EspTokenV2,
30    fee_contract::FeeContract::{self, Deposit},
31    light_client::{
32        BN254::G1Point as G1PointSol,
33        IPlonkVerifier::{PlonkProof as PlonkProofSol, VerifyingKey as VerifyingKeySol},
34        LightClient::{
35            self, LightClientErrors, LightClientInstance, LightClientState as LightClientStateSol,
36            StakeTableState as StakeTableStateSol,
37        },
38    },
39    light_client_mock::{self, LightClientMock},
40    light_client_v2::{self, LightClientV2},
41    light_client_v2_mock::{self, LightClientV2Mock},
42    light_client_v3::{self, LightClientV3},
43    light_client_v3_mock::{self, LightClientV3Mock},
44    ops_timelock::OpsTimelock,
45    ownable_upgradeable::OwnableUpgradeable,
46    plonk_verifier::PlonkVerifier,
47    plonk_verifier_v2::PlonkVerifierV2,
48    plonk_verifier_v3::PlonkVerifierV3,
49    reward_claim::RewardClaim,
50    safe_exit_timelock::SafeExitTimelock,
51    stake_table::StakeTable,
52    stake_table_v2::{self, StakeTableV2},
53    stake_table_v3::{
54        self, BN254::G2Point as G2PointSol, EdOnBN254::EdOnBN254Point as EdOnBN254PointSol,
55        StakeTableV3,
56    },
57};
58
59// For types that we need to interact with some functions but their bindings are not generated
60// we manually declare them there. It's possible that they get included in the future commits,
61// at which point, the rust type system will complain and we simply remove the manual declaration
62// and re-export the type from bindings instead.
63sol! {
64    /// types in src/legacy/Transcript.sol
65    struct TranscriptDataSol {
66        bytes32 state;
67        bytes transcript;
68    }
69
70    /// types in src/libraries/PlonkVerifierV2.sol
71    struct ChallengesSol {
72        uint256 alpha;
73        uint256 alpha2;
74        uint256 alpha3;
75        uint256 beta;
76        uint256 gamma;
77        uint256 zeta;
78        uint256 v;
79        uint256 u;
80    }
81}
82
83// Due to <https://github.com/foundry-rs/foundry/issues/10153> the rust bindings contain duplicate types for our solidity types.
84// In order to avoid writing a lot of boilerplate code we use transmute to convert between these duplicated types.
85// Since all the types we transmute between are generated by foundry from the same underlying solidity type
86// we expect that the order of fields and types of fields are always the same.
87impl From<LightClient::genesisStateReturn> for LightClientStateSol {
88    fn from(v: LightClient::genesisStateReturn) -> Self {
89        unsafe { std::mem::transmute(v) }
90    }
91}
92
93impl From<light_client_mock::LightClient::LightClientState> for LightClientStateSol {
94    fn from(v: light_client_mock::LightClient::LightClientState) -> Self {
95        unsafe { std::mem::transmute(v) }
96    }
97}
98impl From<light_client_mock::LightClientMock::finalizedStateReturn> for LightClientStateSol {
99    fn from(v: light_client_mock::LightClientMock::finalizedStateReturn) -> Self {
100        unsafe { std::mem::transmute(v) }
101    }
102}
103
104impl From<LightClientStateSol> for light_client_mock::LightClient::LightClientState {
105    fn from(v: LightClientStateSol) -> Self {
106        unsafe { std::mem::transmute(v) }
107    }
108}
109
110impl From<PlonkProofSol> for light_client_mock::IPlonkVerifier::PlonkProof {
111    fn from(v: PlonkProofSol) -> Self {
112        unsafe { std::mem::transmute(v) }
113    }
114}
115
116impl From<light_client_mock::LightClientMock::genesisStateReturn> for LightClientStateSol {
117    fn from(v: light_client_mock::LightClientMock::genesisStateReturn) -> Self {
118        unsafe { std::mem::transmute(v) }
119    }
120}
121
122impl From<LightClientV2::finalizedStateReturn> for LightClientStateSol {
123    fn from(v: LightClientV2::finalizedStateReturn) -> Self {
124        unsafe { std::mem::transmute(v) }
125    }
126}
127
128impl From<LightClientV2::votingStakeTableStateReturn> for StakeTableStateSol {
129    fn from(v: LightClientV2::votingStakeTableStateReturn) -> Self {
130        unsafe { std::mem::transmute(v) }
131    }
132}
133
134impl From<light_client_v2_mock::LightClient::LightClientState> for LightClientStateSol {
135    fn from(v: light_client_v2_mock::LightClient::LightClientState) -> Self {
136        unsafe { std::mem::transmute(v) }
137    }
138}
139impl From<LightClientStateSol> for light_client_v2_mock::LightClient::LightClientState {
140    fn from(v: LightClientStateSol) -> Self {
141        unsafe { std::mem::transmute(v) }
142    }
143}
144impl From<LightClientStateSol> for light_client_v2::LightClient::LightClientState {
145    fn from(v: LightClientStateSol) -> Self {
146        unsafe { std::mem::transmute(v) }
147    }
148}
149
150impl From<StakeTableStateSol> for light_client_v2::LightClient::StakeTableState {
151    fn from(v: StakeTableStateSol) -> Self {
152        unsafe { std::mem::transmute(v) }
153    }
154}
155impl From<StakeTableStateSol> for light_client_v2_mock::LightClient::StakeTableState {
156    fn from(v: StakeTableStateSol) -> Self {
157        unsafe { std::mem::transmute(v) }
158    }
159}
160
161impl From<LightClientV2Mock::genesisStateReturn> for LightClientStateSol {
162    fn from(v: LightClientV2Mock::genesisStateReturn) -> Self {
163        unsafe { std::mem::transmute(v) }
164    }
165}
166
167impl From<LightClientV2Mock::finalizedStateReturn> for LightClientStateSol {
168    fn from(v: LightClientV2Mock::finalizedStateReturn) -> Self {
169        unsafe { std::mem::transmute(v) }
170    }
171}
172
173impl From<PlonkProofSol> for light_client_v2::IPlonkVerifier::PlonkProof {
174    fn from(v: PlonkProofSol) -> Self {
175        unsafe { std::mem::transmute(v) }
176    }
177}
178
179impl From<LightClientV2Mock::votingStakeTableStateReturn> for StakeTableStateSol {
180    fn from(v: LightClientV2Mock::votingStakeTableStateReturn) -> Self {
181        unsafe { std::mem::transmute(v) }
182    }
183}
184
185impl From<stake_table_v3::BN254::G1Point> for G1PointSol {
186    fn from(v: stake_table_v3::BN254::G1Point) -> Self {
187        Self { x: v.x, y: v.y }
188    }
189}
190
191impl From<G1PointSol> for stake_table_v3::BN254::G1Point {
192    fn from(v: G1PointSol) -> Self {
193        Self { x: v.x, y: v.y }
194    }
195}
196
197impl PartialEq for StakeTableV3::ValidatorRegistered {
198    fn eq(&self, other: &Self) -> bool {
199        self.account == other.account
200            && self.blsVk == other.blsVk
201            && self.schnorrVk == other.schnorrVk
202            && self.commission == other.commission
203    }
204}
205
206impl PartialEq for StakeTableV3::ValidatorRegisteredV2 {
207    fn eq(&self, other: &Self) -> bool {
208        self.account == other.account
209            && self.blsVK == other.blsVK
210            && self.schnorrVK == other.schnorrVK
211            && self.commission == other.commission
212            && self.blsSig == other.blsSig
213            && self.schnorrSig == other.schnorrSig
214            && self.metadataUri == other.metadataUri
215    }
216}
217
218impl PartialEq for StakeTableV3::ConsensusKeysUpdated {
219    fn eq(&self, other: &Self) -> bool {
220        self.account == other.account
221            && self.blsVK == other.blsVK
222            && self.schnorrVK == other.schnorrVK
223    }
224}
225
226impl PartialEq for StakeTableV3::ConsensusKeysUpdatedV2 {
227    fn eq(&self, other: &Self) -> bool {
228        self.account == other.account
229            && self.blsVK == other.blsVK
230            && self.schnorrVK == other.schnorrVK
231            && self.blsSig == other.blsSig
232            && self.schnorrSig == other.schnorrSig
233    }
234}
235
236impl PartialEq for StakeTableV3::ValidatorRegisteredV3 {
237    fn eq(&self, other: &Self) -> bool {
238        self.account == other.account
239            && self.blsVK == other.blsVK
240            && self.schnorrVK == other.schnorrVK
241            && self.commission == other.commission
242            && self.blsSig == other.blsSig
243            && self.schnorrSig == other.schnorrSig
244            && self.metadataUri == other.metadataUri
245            && self.x25519Key == other.x25519Key
246            && self.p2pAddr == other.p2pAddr
247    }
248}
249
250// Transmute conversion functions for LightClientV3
251impl From<LightClientV3::finalizedStateReturn> for LightClientStateSol {
252    fn from(v: LightClientV3::finalizedStateReturn) -> Self {
253        unsafe { std::mem::transmute(v) }
254    }
255}
256
257impl From<LightClientV3::votingStakeTableStateReturn> for StakeTableStateSol {
258    fn from(v: LightClientV3::votingStakeTableStateReturn) -> Self {
259        unsafe { std::mem::transmute(v) }
260    }
261}
262
263impl From<LightClientStateSol> for light_client_v3::LightClient::LightClientState {
264    fn from(v: LightClientStateSol) -> Self {
265        unsafe { std::mem::transmute(v) }
266    }
267}
268
269impl From<StakeTableStateSol> for light_client_v3::LightClient::StakeTableState {
270    fn from(v: StakeTableStateSol) -> Self {
271        unsafe { std::mem::transmute(v) }
272    }
273}
274
275impl From<PlonkProofSol> for light_client_v3::IPlonkVerifier::PlonkProof {
276    fn from(v: PlonkProofSol) -> Self {
277        unsafe { std::mem::transmute(v) }
278    }
279}
280
281// Transmute conversion functions for LightClientV3Mock
282impl From<light_client_v3_mock::LightClient::LightClientState> for LightClientStateSol {
283    fn from(v: light_client_v3_mock::LightClient::LightClientState) -> Self {
284        unsafe { std::mem::transmute(v) }
285    }
286}
287
288impl From<LightClientStateSol> for light_client_v3_mock::LightClient::LightClientState {
289    fn from(v: LightClientStateSol) -> Self {
290        unsafe { std::mem::transmute(v) }
291    }
292}
293
294impl From<StakeTableStateSol> for light_client_v3_mock::LightClient::StakeTableState {
295    fn from(v: StakeTableStateSol) -> Self {
296        unsafe { std::mem::transmute(v) }
297    }
298}
299
300impl From<LightClientV3Mock::genesisStateReturn> for LightClientStateSol {
301    fn from(v: LightClientV3Mock::genesisStateReturn) -> Self {
302        unsafe { std::mem::transmute(v) }
303    }
304}
305
306impl From<LightClientV3Mock::finalizedStateReturn> for LightClientStateSol {
307    fn from(v: LightClientV3Mock::finalizedStateReturn) -> Self {
308        unsafe { std::mem::transmute(v) }
309    }
310}
311
312impl From<LightClientV3Mock::votingStakeTableStateReturn> for StakeTableStateSol {
313    fn from(v: LightClientV3Mock::votingStakeTableStateReturn) -> Self {
314        unsafe { std::mem::transmute(v) }
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use alloy::{primitives::U256, sol_types::private::Address};
321
322    use crate::sol_types::StakeTableV3::CommissionUpdated;
323
324    #[test]
325    fn test_commission_updated_serde_roundtrip() {
326        let original = CommissionUpdated {
327            validator: Address::random(),
328            timestamp: U256::from(999),
329            oldCommission: 123,
330            newCommission: 456,
331        };
332
333        let serialized = bincode::serialize(&original).expect("Failed to serialize");
334        let deserialized: CommissionUpdated =
335            bincode::deserialize(&serialized).expect("Failed to deserialize");
336
337        assert_eq!(original.validator, deserialized.validator);
338        assert_eq!(original.timestamp, deserialized.timestamp);
339        assert_eq!(original.oldCommission, deserialized.oldCommission);
340        assert_eq!(original.newCommission, deserialized.newCommission);
341    }
342}