feat: Remove generalisation on user approvals manager

This is a too early over generalisation. We need to encode the permit and the signature all together with the other method input variables. This way the return type will be specific to permit2 (instead Vec<u8>) anyway. For simplicity, decided to remove the generalisation and keep things simple

--- don't change below this line ---
ENG-4081 Took 2 hours 23 minutes


Took 9 seconds

Took 1 minute
This commit is contained in:
Diana Carvalho
2025-01-29 17:29:36 +00:00
parent 1b79326637
commit 3a69bbf603
4 changed files with 46 additions and 92 deletions

View File

@@ -7,9 +7,10 @@ use alloy::{
signers::{local::PrivateKeySigner, SignerSync}, signers::{local::PrivateKeySigner, SignerSync},
transports::BoxTransport, transports::BoxTransport,
}; };
use alloy_primitives::{ChainId, U256}; use alloy_primitives::{ChainId, Signature, U256};
use alloy_sol_types::{eip712_domain, sol, SolStruct, SolValue}; use alloy_sol_types::{eip712_domain, sol, SolStruct, SolValue};
use chrono::Utc; use chrono::Utc;
use num_bigint::BigUint;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use tycho_core::Bytes; use tycho_core::Bytes;
@@ -19,7 +20,6 @@ use crate::encoding::{
approvals::protocol_approvals_manager::get_client, approvals::protocol_approvals_manager::get_client,
utils::{biguint_to_u256, bytes_to_address, encode_input}, utils::{biguint_to_u256, bytes_to_address, encode_input},
}, },
user_approvals_manager::{Approval, UserApprovalsManager},
}; };
/// Struct for managing Permit2 operations, including encoding approvals and fetching allowance /// Struct for managing Permit2 operations, including encoding approvals and fetching allowance
@@ -107,34 +107,29 @@ impl Permit2 {
))), ))),
} }
} }
} /// Creates permit single and signature
impl UserApprovalsManager for Permit2 { pub fn get_permit(
/// Encodes multiple approvals into ABI-encoded data and signs them. &self,
fn encode_approvals(&self, approvals: Vec<Approval>) -> Result<Vec<Vec<u8>>, EncodingError> { spender: &Bytes,
owner: &Bytes,
token: &Bytes,
amount: &BigUint,
) -> Result<(PermitSingle, Signature), EncodingError> {
let current_time = Utc::now() let current_time = Utc::now()
.naive_utc() .naive_utc()
.and_utc() .and_utc()
.timestamp() as u64; .timestamp() as u64;
let mut encoded_approvals = Vec::new(); let (_, _, nonce) = self.get_existing_allowance(owner, spender, token)?;
for approval in approvals {
let (_, _, nonce) =
self.get_existing_allowance(&approval.owner, &approval.spender, &approval.token)?;
let expiration = U48::from(current_time + PERMIT_EXPIRATION); let expiration = U48::from(current_time + PERMIT_EXPIRATION);
let sig_deadline = U256::from(current_time + PERMIT_SIG_EXPIRATION); let sig_deadline = U256::from(current_time + PERMIT_SIG_EXPIRATION);
let amount = U160::from(biguint_to_u256(&approval.amount)); let amount = U160::from(biguint_to_u256(amount));
let details = PermitDetails { let details = PermitDetails { token: bytes_to_address(token)?, amount, expiration, nonce };
token: bytes_to_address(&approval.token)?,
amount,
expiration,
nonce,
};
let permit_single = PermitSingle { let permit_single = PermitSingle {
details, details,
spender: bytes_to_address(&approval.spender)?, spender: bytes_to_address(spender)?,
sigDeadline: sig_deadline, sigDeadline: sig_deadline,
}; };
@@ -153,13 +148,7 @@ impl UserApprovalsManager for Permit2 {
e e
)) ))
})?; })?;
let encoded = Ok((permit_single, signature))
(bytes_to_address(&approval.owner)?, permit_single, signature.as_bytes().to_vec())
.abi_encode();
encoded_approvals.push(encoded);
}
Ok(encoded_approvals)
} }
} }
@@ -222,7 +211,7 @@ mod tests {
} }
#[test] #[test]
fn test_encode_approvals() { fn test_get_permit() {
// Set up a mock private key for signing // Set up a mock private key for signing
let private_key = let private_key =
B256::from_str("4c0883a69102937d6231471b5dbb6204fe512961708279feb1be6ae5538da033") B256::from_str("4c0883a69102937d6231471b5dbb6204fe512961708279feb1be6ae5538da033")
@@ -234,21 +223,10 @@ mod tests {
let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap(); let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap();
let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
let amount = BigUint::from(1000u64); let amount = BigUint::from(1000u64);
let approvals =
vec![Approval { owner, spender, token: token.clone(), amount: amount.clone() }];
let encoded_approvals = permit2 let (permit, _) = permit2
.encode_approvals(approvals) .get_permit(&spender, &owner, &token, &amount)
.unwrap(); .unwrap();
assert_eq!(encoded_approvals.len(), 1, "Expected 1 encoded approval");
let encoded = &encoded_approvals[0];
// Remove prefix and owner (first 64 bytes) and signature (last 65 bytes)
let permit_single_encoded = &encoded[64..encoded.len() - 65];
let decoded_permit_single = PermitSingle::abi_decode(permit_single_encoded, false)
.expect("Failed to decode PermitSingle");
let expected_details = PermitDetails { let expected_details = PermitDetails {
token: bytes_to_address(&token).unwrap(), token: bytes_to_address(&token).unwrap(),
@@ -263,7 +241,7 @@ mod tests {
}; };
assert_eq!( assert_eq!(
decoded_permit_single, expected_permit_single, permit, expected_permit_single,
"Decoded PermitSingle does not match expected values" "Decoded PermitSingle does not match expected values"
); );
} }
@@ -309,18 +287,13 @@ mod tests {
assert!(receipt.status(), "Approve transaction failed"); assert!(receipt.status(), "Approve transaction failed");
let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap(); let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap();
let approvals = vec![Approval {
owner: anvil_account.clone(),
spender: spender.clone(),
token: token.clone(),
amount: amount.clone(),
}];
let encoded_approvals = permit2 let (permit, signature) = permit2
.encode_approvals(approvals) .get_permit(&spender, &anvil_account, &token, &amount)
.unwrap(); .unwrap();
let encoded =
let encoded = &encoded_approvals[0]; (bytes_to_address(&anvil_account).unwrap(), permit, signature.as_bytes().to_vec())
.abi_encode();
let function_signature = let function_signature =
"permit(address,((address,uint160,uint48,uint48),address,uint256),bytes)"; "permit(address,((address,uint160,uint48,uint48),address,uint256),bytes)";

View File

@@ -5,4 +5,3 @@ mod models;
mod router_encoder; mod router_encoder;
mod strategy_encoder; mod strategy_encoder;
mod swap_encoder; mod swap_encoder;
mod user_approvals_manager;

View File

@@ -2,11 +2,10 @@ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
models::{Solution, Transaction}, models::{Solution, Transaction},
strategy_encoder::StrategySelector, strategy_encoder::StrategySelector,
user_approvals_manager::UserApprovalsManager,
}; };
#[allow(dead_code)] #[allow(dead_code)]
pub trait RouterEncoder<S: StrategySelector, A: UserApprovalsManager> { pub trait RouterEncoder<S: StrategySelector> {
fn encode_router_calldata( fn encode_router_calldata(
&self, &self,
solutions: Vec<Solution>, solutions: Vec<Solution>,

View File

@@ -1,17 +0,0 @@
use num_bigint::BigUint;
use tycho_core::Bytes;
use crate::encoding::errors::EncodingError;
#[allow(dead_code)]
pub struct Approval {
pub spender: Bytes,
pub owner: Bytes,
pub token: Bytes,
pub amount: BigUint,
}
pub trait UserApprovalsManager {
#[allow(dead_code)]
fn encode_approvals(&self, approvals: Vec<Approval>) -> Result<Vec<Vec<u8>>, EncodingError>;
}