Skip to content

Commit

Permalink
Add method to retrieve event selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
danhper committed Aug 9, 2024
1 parent d52216c commit 301450c
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 7 deletions.
6 changes: 6 additions & 0 deletions docs/src/builtin_methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ The first element of the tuple is the function signature, and the second element
("transfer(address,uint256)", (0x789f8F7B547183Ab8E99A5e0E6D567E90e0EB03B, 100000000000000000000))
```

## `Event` static methods

### `Event.selector -> bytes32`

Returns the selector (aka topic0) of the given event

## `num` (`uint*` and `int*`) static methods

### `type(num).max -> num`
Expand Down
7 changes: 7 additions & 0 deletions docs/src/interacting_with_contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,10 @@ The `events.fetch` method accepts the following options:
By default, it will try to fetch from the first ever block to the latest block.
In many cases, the RPC provider will reject the request because too much data would be returned, in which case
options above will need to be added to restrict the size of the response.

To only get one type of event, e.g. `Transfer`, you can filter using `topic0` and the selector of the desired event.

```javascript
>> events.fetch{fromBlock: 20490506, toBlock: 20490512, topic0: ERC20.Approval.selector}(0xe07F9D810a48ab5c3c914BA3cA53AF14E4491e8A)[0]
Log { address: 0xe07F9D810a48ab5c3c914BA3cA53AF14E4491e8A, topics: [0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000008149dc18d39fdba137e43c871e7801e7cf566d41, 0x000000000000000000000000ea50f402653c41cadbafd1f788341db7b7f37816], data: 0x000000000000000000000000000000000000000000000025f273933db5700000, args: Approval { owner: 0x8149DC18D39FDBa137E43C871e7801E7CF566D41, spender: 0xeA50f402653c41cAdbaFD1f788341dB7B7F37816, value: 700000000000000000000 } }
```
22 changes: 22 additions & 0 deletions src/interpreter/builtins/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::sync::Arc;

use crate::interpreter::{
functions::{FunctionDef, SyncProperty},
Env, Type, Value,
};
use anyhow::{bail, Result};
use lazy_static::lazy_static;

pub fn event_selector(_env: &Env, receiver: &Value) -> Result<Value> {
let event_abi = match receiver {
Value::TypeObject(Type::Event(event)) => event,
_ => bail!("selector function expects receiver to be an event"),
};

Ok(event_abi.selector().into())
}

lazy_static! {
pub static ref EVENT_SELECTOR: Arc<dyn FunctionDef> =
SyncProperty::arc("selector", event_selector);
}
9 changes: 7 additions & 2 deletions src/interpreter/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod address;
mod block;
mod concat;
mod console;
mod event;
mod events;
mod format;
mod iterable;
Expand Down Expand Up @@ -135,8 +136,12 @@ lazy_static! {
m.insert(NonParametricType::Console, console_methods);

let mut event_methods = HashMap::new();
event_methods.insert("fetch".to_string(), events::FETCH_EVENTS.clone());
m.insert(NonParametricType::Events, event_methods);
event_methods.insert("selector".to_string(), event::EVENT_SELECTOR.clone());
m.insert(NonParametricType::Event, event_methods);

let mut events_methods = HashMap::new();
events_methods.insert("fetch".to_string(), events::FETCH_EVENTS.clone());
m.insert(NonParametricType::Events, events_methods);

let mut repl_methods = HashMap::new();
repl_methods.insert("vars".to_string(), repl::REPL_LIST_VARS.clone());
Expand Down
38 changes: 33 additions & 5 deletions src/interpreter/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloy::{
json_abi::JsonAbi,
primitives::{Address, B256, I256, U160, U256},
};
use anyhow::{bail, Result};
use anyhow::{anyhow, bail, Result};
use indexmap::IndexMap;
use itertools::Itertools;
use solang_parser::pt as parser;
Expand Down Expand Up @@ -61,12 +61,28 @@ impl ContractInfo {
let _func = self
.1
.function(name)
.ok_or_else(|| anyhow::anyhow!("function {} not found in contract {}", name, self.0))?;
.ok_or_else(|| anyhow!("function {} not found in contract {}", name, self.0))?;
Ok(Function::new(
ContractFunction::arc(name),
Some(&Value::Contract(self.clone(), addr)),
))
}

pub fn member_access(&self, name: &str) -> Result<Value> {
if let Some(event) = self.1.events.get(name).and_then(|v| v.first()) {
return Ok(Value::TypeObject(Type::Event(event.clone())));
}
let func = STATIC_METHODS
.get(&NonParametricType::Contract)
.unwrap()
.get(name)
.ok_or(anyhow!("{} not found in contract {}", name, self.0))?;
Ok(Function::method(
func.clone(),
&Value::TypeObject(Type::Contract(self.clone())),
)
.into())
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -86,6 +102,7 @@ pub enum NonParametricType {
Tuple,
Mapping,
Contract,
Event,
Transaction,
TransactionReceipt,
Function,
Expand Down Expand Up @@ -114,6 +131,7 @@ pub enum Type {
Tuple(Vec<Type>),
Mapping(Box<Type>, Box<Type>),
Contract(ContractInfo),
Event(alloy::json_abi::Event),
Transaction,
TransactionReceipt,
Function,
Expand Down Expand Up @@ -149,6 +167,7 @@ impl Display for Type {
}
Type::Mapping(k, v) => write!(f, "mapping({} => {})", k, v),
Type::Contract(ContractInfo(name, _)) => write!(f, "{}", name),
Type::Event(event) => write!(f, "{}", event.full_signature()),
Type::Function => write!(f, "function"),

Type::Transaction => write!(f, "Transaction"),
Expand Down Expand Up @@ -182,6 +201,7 @@ impl<T: AsRef<Type>> From<T> for NonParametricType {
Type::Tuple(_) => NonParametricType::Tuple,
Type::Mapping(..) => NonParametricType::Mapping,
Type::Contract(..) => NonParametricType::Contract,
Type::Event(..) => NonParametricType::Event,
Type::Function => NonParametricType::Function,
Type::Transaction => NonParametricType::Transaction,
Type::TransactionReceipt => NonParametricType::TransactionReceipt,
Expand Down Expand Up @@ -494,9 +514,17 @@ impl Type {
abi.functions.keys().map(|s| s.to_string()).collect()
}
Type::NamedTuple(_, fields) => fields.0.keys().map(|s| s.to_string()).collect(),
Type::Type(type_) => STATIC_METHODS
.get(&type_.into())
.map_or(vec![], |m| m.keys().cloned().collect()),
Type::Type(type_) => {
let mut static_methods = STATIC_METHODS
.get(&type_.into())
.map_or(vec![], |m| m.keys().cloned().collect());

if let Type::Contract(ContractInfo(_, abi)) = type_.as_ref() {
static_methods.extend(abi.events.keys().map(|s| s.to_string()));
}

static_methods
}
_ => INSTANCE_METHODS
.get(&self.into())
.map_or(vec![], |m| m.keys().cloned().collect()),
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ impl Value {
}
Value::Contract(c, addr) => c.make_function(member, *addr).map(Into::into),
Value::Func(f) => f.member_access(member),
Value::TypeObject(Type::Contract(c)) => c.member_access(member),
_ => {
let (type_, methods) = match self {
Value::TypeObject(Type::Type(type_)) => {
Expand Down

0 comments on commit 301450c

Please sign in to comment.