Skip to content

Commit 8c2c5ae

Browse files
feat: hooks necessary files
Signed-off-by: Ivaylo Nikolov <[email protected]>
1 parent f45e3cd commit 8c2c5ae

14 files changed

+1461
-0
lines changed

src/hooks/evm_hook_call.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use hedera_proto::services;
2+
3+
use crate::{
4+
FromProtobuf,
5+
ToProtobuf,
6+
};
7+
8+
/// An EVM hook call.
9+
#[derive(Debug, Clone, PartialEq, Eq)]
10+
pub struct EvmHookCall {
11+
/// The call data for the EVM hook.
12+
pub call_data: Option<Vec<u8>>,
13+
/// The gas limit for the hook call.
14+
pub gas_limit: Option<u64>,
15+
}
16+
17+
impl EvmHookCall {
18+
/// Create a new `EvmHookCall`.
19+
pub fn new(call_data: Option<Vec<u8>>) -> Self {
20+
Self { call_data, gas_limit: None }
21+
}
22+
23+
/// Set the call data for the hook.
24+
pub fn set_call_data(&mut self, call_data: Vec<u8>) -> &mut Self {
25+
self.call_data = Some(call_data);
26+
self
27+
}
28+
29+
/// Set the gas limit for the hook call.
30+
pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
31+
self.gas_limit = Some(gas_limit);
32+
self
33+
}
34+
}
35+
36+
impl ToProtobuf for EvmHookCall {
37+
type Protobuf = services::EvmHookCall;
38+
39+
fn to_protobuf(&self) -> Self::Protobuf {
40+
services::EvmHookCall {
41+
data: self.call_data.clone().unwrap_or_default(),
42+
gas_limit: self.gas_limit.unwrap_or(0),
43+
}
44+
}
45+
}
46+
47+
impl FromProtobuf<services::EvmHookCall> for EvmHookCall {
48+
fn from_protobuf(pb: services::EvmHookCall) -> crate::Result<Self> {
49+
Ok(Self {
50+
call_data: if pb.data.is_empty() { None } else { Some(pb.data) },
51+
gas_limit: if pb.gas_limit == 0 { None } else { Some(pb.gas_limit) },
52+
})
53+
}
54+
}
55+
56+
#[cfg(test)]
57+
mod tests {
58+
use super::*;
59+
60+
#[test]
61+
fn test_evm_hook_call_creation() {
62+
let call_data = vec![1, 2, 3, 4, 5];
63+
let hook_call = EvmHookCall::new(Some(call_data.clone()));
64+
65+
assert_eq!(hook_call.call_data, Some(call_data));
66+
}
67+
68+
#[test]
69+
fn test_evm_hook_call_setters() {
70+
let mut hook_call = EvmHookCall::new(None);
71+
let call_data = vec![6, 7, 8, 9, 10];
72+
73+
hook_call.set_call_data(call_data.clone());
74+
assert_eq!(hook_call.call_data, Some(call_data));
75+
}
76+
77+
#[test]
78+
fn test_evm_hook_call_protobuf_roundtrip() {
79+
let call_data = vec![11, 12, 13, 14, 15];
80+
let original = EvmHookCall::new(Some(call_data));
81+
82+
let protobuf = original.to_protobuf();
83+
let reconstructed = EvmHookCall::from_protobuf(protobuf).unwrap();
84+
85+
assert_eq!(original, reconstructed);
86+
}
87+
}

src/hooks/evm_hook_spec.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use hedera_proto::services;
2+
3+
use crate::contract::ContractId;
4+
use crate::{
5+
FromProtobuf,
6+
ToProtobuf,
7+
};
8+
9+
/// Shared specifications for an EVM hook. May be used for any extension point.
10+
#[derive(Debug, Clone, PartialEq, Eq)]
11+
pub struct EvmHookSpec {
12+
/// The id of a contract that implements the extension point API with EVM bytecode.
13+
pub contract_id: Option<ContractId>,
14+
}
15+
16+
impl EvmHookSpec {
17+
/// Create a new `EvmHookSpec`.
18+
pub fn new(contract_id: Option<ContractId>) -> Self {
19+
Self { contract_id }
20+
}
21+
}
22+
23+
impl ToProtobuf for EvmHookSpec {
24+
type Protobuf = services::EvmHookSpec;
25+
26+
fn to_protobuf(&self) -> Self::Protobuf {
27+
services::EvmHookSpec {
28+
bytecode_source: self
29+
.contract_id
30+
.as_ref()
31+
.map(|id| services::evm_hook_spec::BytecodeSource::ContractId(id.to_protobuf())),
32+
}
33+
}
34+
}
35+
36+
impl FromProtobuf<services::EvmHookSpec> for EvmHookSpec {
37+
fn from_protobuf(pb: services::EvmHookSpec) -> crate::Result<Self> {
38+
let contract_id = match pb.bytecode_source {
39+
Some(services::evm_hook_spec::BytecodeSource::ContractId(id)) => {
40+
Some(ContractId::from_protobuf(id)?)
41+
}
42+
None => None,
43+
};
44+
45+
Ok(Self { contract_id })
46+
}
47+
}
48+
49+
#[cfg(test)]
50+
mod tests {
51+
use super::*;
52+
use crate::contract::ContractId;
53+
54+
#[test]
55+
fn test_evm_hook_spec_creation() {
56+
let contract_id = ContractId::new(0, 0, 123);
57+
let spec = EvmHookSpec::new(Some(contract_id.clone()));
58+
59+
assert_eq!(spec.contract_id, Some(contract_id));
60+
}
61+
62+
#[test]
63+
fn test_evm_hook_spec_protobuf_roundtrip() {
64+
let contract_id = ContractId::new(0, 0, 456);
65+
let original = EvmHookSpec::new(Some(contract_id));
66+
let protobuf = original.to_protobuf();
67+
let reconstructed = EvmHookSpec::from_protobuf(protobuf).unwrap();
68+
69+
assert_eq!(original, reconstructed);
70+
}
71+
}

src/hooks/fungible_hook_call.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use hedera_proto::services;
2+
3+
use crate::hooks::{
4+
EvmHookCall,
5+
FungibleHookType,
6+
HookCall,
7+
};
8+
use crate::{
9+
FromProtobuf,
10+
ToProtobuf,
11+
};
12+
13+
/// A typed hook call for fungible (HBAR and FT) transfers.
14+
#[derive(Debug, Clone, PartialEq, Eq)]
15+
pub struct FungibleHookCall {
16+
/// The underlying hook call data.
17+
pub hook_call: HookCall,
18+
/// The type of fungible hook.
19+
pub hook_type: FungibleHookType,
20+
}
21+
22+
impl FungibleHookCall {
23+
/// Create a new `FungibleHookCall`.
24+
pub fn new(hook_call: HookCall, hook_type: FungibleHookType) -> Self {
25+
Self { hook_call, hook_type }
26+
}
27+
28+
/// Internal method to create from protobuf with a known type.
29+
pub(crate) fn from_protobuf_with_type(
30+
pb: services::HookCall,
31+
hook_type: FungibleHookType,
32+
) -> crate::Result<Self> {
33+
Ok(Self { hook_call: HookCall::from_protobuf(pb)?, hook_type })
34+
}
35+
}
36+
37+
impl ToProtobuf for FungibleHookCall {
38+
type Protobuf = services::HookCall;
39+
40+
fn to_protobuf(&self) -> Self::Protobuf {
41+
self.hook_call.to_protobuf()
42+
}
43+
}
44+
45+
#[cfg(test)]
46+
mod tests {
47+
use super::*;
48+
49+
#[test]
50+
fn test_fungible_hook_call_creation() {
51+
let hook_id = 123;
52+
let hook_type = FungibleHookType::PreTxAllowanceHook;
53+
let mut hook_call_obj = HookCall::new(None, None);
54+
hook_call_obj.set_hook_id(hook_id);
55+
let hook_call = FungibleHookCall::new(hook_call_obj, hook_type);
56+
57+
assert_eq!(hook_call.hook_call.hook_id, Some(hook_id));
58+
assert_eq!(hook_call.hook_type, hook_type);
59+
}
60+
61+
#[test]
62+
fn test_fungible_hook_call_with_call() {
63+
let call_data = vec![1, 2, 3, 4, 5];
64+
let evm_call = EvmHookCall::new(Some(call_data));
65+
let hook_type = FungibleHookType::PrePostTxAllowanceHook;
66+
let mut hook_call_obj = HookCall::new(None, None);
67+
hook_call_obj.set_call(evm_call.clone());
68+
let hook_call = FungibleHookCall::new(hook_call_obj, hook_type);
69+
70+
assert_eq!(hook_call.hook_call.call, Some(evm_call));
71+
assert_eq!(hook_call.hook_type, hook_type);
72+
}
73+
}

src/hooks/fungible_hook_type.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/// Types of fungible (HBAR and FT) hooks.
2+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3+
#[repr(u8)]
4+
pub enum FungibleHookType {
5+
/// A single call made before attempting the transfer.
6+
PreTxAllowanceHook = 0,
7+
/// Two calls - first before attempting the transfer (allowPre), and second after
8+
/// attempting the transfer (allowPost).
9+
PrePostTxAllowanceHook = 1,
10+
}
11+
12+
impl FungibleHookType {
13+
/// Returns the numeric value of the hook type.
14+
pub fn value(&self) -> u8 {
15+
*self as u8
16+
}
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use super::*;
22+
23+
#[test]
24+
fn test_fungible_hook_type_values() {
25+
assert_eq!(FungibleHookType::PreTxAllowanceHook.value(), 0);
26+
assert_eq!(FungibleHookType::PrePostTxAllowanceHook.value(), 1);
27+
}
28+
}

0 commit comments

Comments
 (0)