mirror of
https://github.com/serai-dex/serai.git
synced 2025-02-03 03:36:35 +00:00
Smash ERC20 into its own library
This commit is contained in:
parent
ae61f3d359
commit
a7d5640642
9 changed files with 108 additions and 10 deletions
1
.github/workflows/tests.yml
vendored
1
.github/workflows/tests.yml
vendored
|
@ -55,6 +55,7 @@ jobs:
|
|||
-p serai-processor-ethereum-contracts \
|
||||
-p serai-processor-ethereum-primitives \
|
||||
-p serai-processor-ethereum-deployer \
|
||||
-p serai-processor-ethereum-erc20 \
|
||||
-p ethereum-serai \
|
||||
-p serai-ethereum-processor \
|
||||
-p serai-monero-processor \
|
||||
|
|
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -8737,6 +8737,19 @@ dependencies = [
|
|||
"serai-processor-ethereum-primitives",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-processor-ethereum-erc20"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy-core",
|
||||
"alloy-provider",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-simple-request-transport",
|
||||
"alloy-sol-macro",
|
||||
"alloy-sol-types",
|
||||
"alloy-transport",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-processor-ethereum-primitives"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -90,6 +90,7 @@ members = [
|
|||
"processor/ethereum/contracts",
|
||||
"processor/ethereum/primitives",
|
||||
"processor/ethereum/deployer",
|
||||
"processor/ethereum/erc20",
|
||||
"processor/ethereum/ethereum-serai",
|
||||
"processor/ethereum",
|
||||
"processor/monero",
|
||||
|
|
|
@ -62,6 +62,7 @@ exceptions = [
|
|||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-contracts" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-primitives" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-deployer" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-processor-ethereum-erc20" },
|
||||
{ allow = ["AGPL-3.0"], name = "ethereum-serai" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-ethereum-processor" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-monero-processor" },
|
||||
|
|
28
processor/ethereum/erc20/Cargo.toml
Normal file
28
processor/ethereum/erc20/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
|||
[package]
|
||||
name = "serai-processor-ethereum-erc20"
|
||||
version = "0.1.0"
|
||||
description = "A library for the Serai Processor to interact with ERC20s"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/processor/ethereum/erc20"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
rust-version = "1.79"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
alloy-core = { version = "0.8", default-features = false }
|
||||
|
||||
alloy-sol-types = { version = "0.8", default-features = false }
|
||||
alloy-sol-macro = { version = "0.8", default-features = false }
|
||||
|
||||
alloy-rpc-types-eth = { version = "0.3", default-features = false }
|
||||
alloy-transport = { version = "0.3", default-features = false }
|
||||
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||
alloy-provider = { version = "0.3", default-features = false }
|
15
processor/ethereum/erc20/LICENSE
Normal file
15
processor/ethereum/erc20/LICENSE
Normal file
|
@ -0,0 +1,15 @@
|
|||
AGPL-3.0-only license
|
||||
|
||||
Copyright (c) 2022-2024 Luke Parker
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License Version 3 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
3
processor/ethereum/erc20/README.md
Normal file
3
processor/ethereum/erc20/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# ERC20
|
||||
|
||||
A library for the Serai Processor to interact with ERC20s.
|
|
@ -1,3 +1,7 @@
|
|||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use std::{sync::Arc, collections::HashSet};
|
||||
|
||||
use alloy_core::primitives::{Address, B256, U256};
|
||||
|
@ -5,18 +9,31 @@ use alloy_core::primitives::{Address, B256, U256};
|
|||
use alloy_sol_types::{SolInterface, SolEvent};
|
||||
|
||||
use alloy_rpc_types_eth::Filter;
|
||||
use alloy_transport::{TransportErrorKind, RpcError};
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
use crate::Error;
|
||||
pub use crate::abi::erc20 as abi;
|
||||
use abi::{IERC20Calls, Transfer, transferCall, transferFromCall};
|
||||
#[rustfmt::skip]
|
||||
#[expect(warnings)]
|
||||
#[expect(needless_pass_by_value)]
|
||||
#[expect(clippy::all)]
|
||||
#[expect(clippy::ignored_unit_patterns)]
|
||||
#[expect(clippy::redundant_closure_for_method_calls)]
|
||||
mod abi {
|
||||
alloy_sol_macro::sol!("contracts/IERC20.sol");
|
||||
}
|
||||
use abi::IERC20::{IERC20Calls, Transfer, transferCall, transferFromCall};
|
||||
|
||||
/// A top-level ERC20 transfer
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TopLevelErc20Transfer {
|
||||
/// The transaction ID which effected this transfer.
|
||||
pub id: [u8; 32],
|
||||
/// The address which made the transfer.
|
||||
pub from: [u8; 20],
|
||||
/// The amount transferred.
|
||||
pub amount: U256,
|
||||
/// The data appended after the call itself.
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
|
@ -29,30 +46,43 @@ impl Erc20 {
|
|||
Self(provider, Address::from(&address))
|
||||
}
|
||||
|
||||
/// Fetch all top-level transfers to the specified ERC20.
|
||||
pub async fn top_level_transfers(
|
||||
&self,
|
||||
block: u64,
|
||||
to: [u8; 20],
|
||||
) -> Result<Vec<TopLevelErc20Transfer>, Error> {
|
||||
) -> Result<Vec<TopLevelErc20Transfer>, RpcError<TransportErrorKind>> {
|
||||
let filter = Filter::new().from_block(block).to_block(block).address(self.1);
|
||||
let filter = filter.event_signature(Transfer::SIGNATURE_HASH);
|
||||
let mut to_topic = [0; 32];
|
||||
to_topic[12 ..].copy_from_slice(&to);
|
||||
let filter = filter.topic2(B256::from(to_topic));
|
||||
let logs = self.0.get_logs(&filter).await.map_err(|_| Error::ConnectionError)?;
|
||||
let logs = self.0.get_logs(&filter).await?;
|
||||
|
||||
/*
|
||||
A set of all transactions we've handled a transfer from. This handles the edge case where a
|
||||
top-level transfer T somehow triggers another transfer T', with equivalent contents, within
|
||||
the same transaction. We only want to report one transfer as only one is top-level.
|
||||
*/
|
||||
let mut handled = HashSet::new();
|
||||
|
||||
let mut top_level_transfers = vec![];
|
||||
for log in logs {
|
||||
// Double check the address which emitted this log
|
||||
if log.address() != self.1 {
|
||||
Err(Error::ConnectionError)?;
|
||||
Err(TransportErrorKind::Custom(
|
||||
"node returned logs for a different address than requested".to_string().into(),
|
||||
))?;
|
||||
}
|
||||
|
||||
let tx_id = log.transaction_hash.ok_or(Error::ConnectionError)?;
|
||||
let tx =
|
||||
self.0.get_transaction_by_hash(tx_id).await.ok().flatten().ok_or(Error::ConnectionError)?;
|
||||
let tx_id = log.transaction_hash.ok_or_else(|| {
|
||||
TransportErrorKind::Custom("log didn't specify its transaction hash".to_string().into())
|
||||
})?;
|
||||
let tx = self.0.get_transaction_by_hash(tx_id).await?.ok_or_else(|| {
|
||||
TransportErrorKind::Custom(
|
||||
"node didn't have the transaction which emitted a log it had".to_string().into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
// If this is a top-level call...
|
||||
if tx.to == Some(self.1) {
|
||||
|
@ -70,7 +100,13 @@ impl Erc20 {
|
|||
_ => continue,
|
||||
};
|
||||
|
||||
let log = log.log_decode::<Transfer>().map_err(|_| Error::ConnectionError)?.inner.data;
|
||||
let log = log
|
||||
.log_decode::<Transfer>()
|
||||
.map_err(|e| {
|
||||
TransportErrorKind::Custom(format!("failed to decode Transfer log: {e:?}").into())
|
||||
})?
|
||||
.inner
|
||||
.data;
|
||||
|
||||
// Ensure the top-level transfer is equivalent, and this presumably isn't a log for an
|
||||
// internal transfer
|
Loading…
Reference in a new issue