Merge pull request #12 from propeller-heads/encoding/dc/ENG-4064-protocol-approvals
feat: Implement ProtocolApprovalsManager
This commit is contained in:
88
Cargo.lock
generated
88
Cargo.lock
generated
@@ -30,6 +30,15 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
@@ -49,6 +58,7 @@ dependencies = [
|
||||
"alloy-network",
|
||||
"alloy-provider",
|
||||
"alloy-rpc-client",
|
||||
"alloy-rpc-types",
|
||||
"alloy-serde",
|
||||
"alloy-transport",
|
||||
"alloy-transport-http",
|
||||
@@ -332,6 +342,18 @@ dependencies = [
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-rpc-types"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eea9bf1abdd506f985a53533f5ac01296bcd6102c5e139bbc5d40bc468d2c916"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-serde",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-rpc-types-eth"
|
||||
version = "0.5.4"
|
||||
@@ -1247,6 +1269,12 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
@@ -2260,12 +2288,41 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.12"
|
||||
@@ -2326,6 +2383,36 @@ dependencies = [
|
||||
"rustc-hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89"
|
||||
dependencies = [
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
"rstest_macros",
|
||||
"rustc_version 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_macros"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"glob",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version 0.4.1",
|
||||
"syn 2.0.96",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruint"
|
||||
version = "1.12.4"
|
||||
@@ -3049,6 +3136,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"rstest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
|
||||
@@ -14,11 +14,14 @@ serde_json = "1.0.135"
|
||||
thiserror = "1.0.69"
|
||||
tokio = { version = "1.38.0", features = ["full"] }
|
||||
|
||||
alloy = { version = "0.5.4", features = ["providers"], optional = true }
|
||||
alloy = { version = "0.5.4", features = ["providers", "rpc-types-eth"], optional = true }
|
||||
alloy-sol-types = { version = "0.8.14", optional = true }
|
||||
alloy-primitives = { version = "0.8.9", optional = true }
|
||||
tycho-core = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-core", tag = "0.46.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = "0.24.0"
|
||||
|
||||
[features]
|
||||
default = ["evm"]
|
||||
evm = ["alloy", "alloy-sol-types", "alloy-primitives"]
|
||||
|
||||
@@ -1,49 +1,99 @@
|
||||
use std::{env, sync::Arc};
|
||||
|
||||
use alloy::{
|
||||
providers::{ProviderBuilder, RootProvider},
|
||||
providers::{Provider, ProviderBuilder, RootProvider},
|
||||
rpc::types::{TransactionInput, TransactionRequest},
|
||||
transports::BoxTransport,
|
||||
};
|
||||
use alloy_primitives::Address;
|
||||
use alloy_primitives::{Address, Bytes, TxKind, U256};
|
||||
use alloy_sol_types::SolValue;
|
||||
use dotenv::dotenv;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::encoding::{errors::EncodingError, evm::utils::encode_input};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ProtocolApprovalsManager {
|
||||
client: Arc<RootProvider<BoxTransport>>,
|
||||
runtime: Runtime,
|
||||
}
|
||||
impl ProtocolApprovalsManager {
|
||||
pub fn new() -> Self {
|
||||
Self { client: get_client() }
|
||||
let runtime = Runtime::new().expect("Failed to create runtime");
|
||||
let client = runtime.block_on(get_client());
|
||||
Self { client, runtime }
|
||||
}
|
||||
pub async fn approval_needed(
|
||||
pub fn approval_needed(
|
||||
&self,
|
||||
_token: Address,
|
||||
_spender_address: Address,
|
||||
_router_address: Address,
|
||||
) -> bool {
|
||||
todo!()
|
||||
// should be something like
|
||||
// let allowance = self
|
||||
// .client
|
||||
// .call(token, "allowance(address,address)(uint256)", (router_address,
|
||||
// spender_address)) .await;
|
||||
//
|
||||
// allowance == U256::ZERO // If allowance is 0, approval is needed
|
||||
token: Address,
|
||||
owner_address: Address,
|
||||
spender_address: Address,
|
||||
) -> Result<bool, EncodingError> {
|
||||
let args = (owner_address, spender_address);
|
||||
let data = encode_input("allowance(address,address)", args.abi_encode());
|
||||
let tx = TransactionRequest {
|
||||
to: Some(TxKind::from(token)),
|
||||
input: TransactionInput { input: Some(Bytes::from(data)), data: None },
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let output = self
|
||||
.runtime
|
||||
.block_on(async { self.client.call(&tx).await });
|
||||
match output {
|
||||
Ok(response) => {
|
||||
let allowance: U256 = U256::abi_decode(&response, true).map_err(|_| {
|
||||
EncodingError::FatalError("Failed to decode response for allowance".to_string())
|
||||
})?;
|
||||
|
||||
Ok(allowance.is_zero())
|
||||
}
|
||||
Err(err) => Err(EncodingError::RecoverableError(format!(
|
||||
"Allowance call failed with error: {:?}",
|
||||
err
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_client() -> Arc<RootProvider<BoxTransport>> {
|
||||
pub async fn get_client() -> Arc<RootProvider<BoxTransport>> {
|
||||
dotenv().ok();
|
||||
let eth_rpc_url = env::var("ETH_RPC_URL").expect("Missing ETH_RPC_URL in environment");
|
||||
let runtime = tokio::runtime::Handle::try_current()
|
||||
.is_err()
|
||||
.then(|| tokio::runtime::Runtime::new().unwrap())
|
||||
.unwrap();
|
||||
let client = runtime.block_on(async {
|
||||
ProviderBuilder::new()
|
||||
.on_builtin(ð_rpc_url)
|
||||
.await
|
||||
.unwrap()
|
||||
});
|
||||
let client = ProviderBuilder::new()
|
||||
.on_builtin(ð_rpc_url)
|
||||
.await
|
||||
.expect("Failed to build provider");
|
||||
Arc::new(client)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
#[rstest]
|
||||
#[case::approval_not_needed(
|
||||
"0xba12222222228d8ba445958a75a0704d566bf2c8",
|
||||
"0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4",
|
||||
false
|
||||
)]
|
||||
#[case::approval_needed(
|
||||
"0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4",
|
||||
"0xba12222222228d8ba445958a75a0704d566bf2c8",
|
||||
true
|
||||
)]
|
||||
fn test_approval_needed(#[case] spender: &str, #[case] owner: &str, #[case] expected: bool) {
|
||||
let manager = ProtocolApprovalsManager::new();
|
||||
|
||||
let token = Address::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let spender = Address::from_str(spender).unwrap();
|
||||
let owner = Address::from_str(owner).unwrap();
|
||||
|
||||
let result = manager
|
||||
.approval_needed(token, owner, spender)
|
||||
.unwrap();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,17 +53,10 @@ impl SwapEncoder for BalancerV2SwapEncoder {
|
||||
encoding_context: EncodingContext,
|
||||
) -> Result<Vec<u8>, EncodingError> {
|
||||
let token_approvals_manager = ProtocolApprovalsManager::new();
|
||||
let runtime = tokio::runtime::Handle::try_current()
|
||||
.is_err()
|
||||
.then(|| tokio::runtime::Runtime::new().unwrap())
|
||||
.unwrap();
|
||||
let token = bytes_to_address(&swap.token_in)?;
|
||||
let router_address = bytes_to_address(&encoding_context.address_for_approvals)?;
|
||||
let approval_needed = runtime.block_on(async {
|
||||
token_approvals_manager
|
||||
.approval_needed(token, self.vault_address, router_address)
|
||||
.await
|
||||
});
|
||||
let approval_needed =
|
||||
token_approvals_manager.approval_needed(token, router_address, self.vault_address)?;
|
||||
// should we return gas estimation here too?? if there is an approval needed, gas will be
|
||||
// higher.
|
||||
let args = (
|
||||
|
||||
Reference in New Issue
Block a user