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},
transports::BoxTransport,
};
use alloy_primitives::{ChainId, U256};
use alloy_primitives::{ChainId, Signature, U256};
use alloy_sol_types::{eip712_domain, sol, SolStruct, SolValue};
use chrono::Utc;
use num_bigint::BigUint;
use tokio::runtime::Runtime;
use tycho_core::Bytes;
@@ -19,7 +20,6 @@ use crate::encoding::{
approvals::protocol_approvals_manager::get_client,
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
@@ -107,59 +107,48 @@ impl Permit2 {
))),
}
}
}
impl UserApprovalsManager for Permit2 {
/// Encodes multiple approvals into ABI-encoded data and signs them.
fn encode_approvals(&self, approvals: Vec<Approval>) -> Result<Vec<Vec<u8>>, EncodingError> {
/// Creates permit single and signature
pub fn get_permit(
&self,
spender: &Bytes,
owner: &Bytes,
token: &Bytes,
amount: &BigUint,
) -> Result<(PermitSingle, Signature), EncodingError> {
let current_time = Utc::now()
.naive_utc()
.and_utc()
.timestamp() as u64;
let mut encoded_approvals = Vec::new();
let (_, _, nonce) = self.get_existing_allowance(owner, spender, token)?;
let expiration = U48::from(current_time + PERMIT_EXPIRATION);
let sig_deadline = U256::from(current_time + PERMIT_SIG_EXPIRATION);
let amount = U160::from(biguint_to_u256(amount));
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 sig_deadline = U256::from(current_time + PERMIT_SIG_EXPIRATION);
let amount = U160::from(biguint_to_u256(&approval.amount));
let details = PermitDetails { token: bytes_to_address(token)?, amount, expiration, nonce };
let details = PermitDetails {
token: bytes_to_address(&approval.token)?,
amount,
expiration,
nonce,
};
let permit_single = PermitSingle {
details,
spender: bytes_to_address(spender)?,
sigDeadline: sig_deadline,
};
let permit_single = PermitSingle {
details,
spender: bytes_to_address(&approval.spender)?,
sigDeadline: sig_deadline,
};
let domain = eip712_domain! {
name: "Permit2",
chain_id: self.chain_id,
verifying_contract: self.address,
};
let hash = permit_single.eip712_signing_hash(&domain);
let signature = self
.signer
.sign_hash_sync(&hash)
.map_err(|e| {
EncodingError::FatalError(format!(
"Failed to sign permit2 approval with error: {}",
e
))
})?;
let encoded =
(bytes_to_address(&approval.owner)?, permit_single, signature.as_bytes().to_vec())
.abi_encode();
encoded_approvals.push(encoded);
}
Ok(encoded_approvals)
let domain = eip712_domain! {
name: "Permit2",
chain_id: self.chain_id,
verifying_contract: self.address,
};
let hash = permit_single.eip712_signing_hash(&domain);
let signature = self
.signer
.sign_hash_sync(&hash)
.map_err(|e| {
EncodingError::FatalError(format!(
"Failed to sign permit2 approval with error: {}",
e
))
})?;
Ok((permit_single, signature))
}
}
@@ -222,7 +211,7 @@ mod tests {
}
#[test]
fn test_encode_approvals() {
fn test_get_permit() {
// Set up a mock private key for signing
let private_key =
B256::from_str("4c0883a69102937d6231471b5dbb6204fe512961708279feb1be6ae5538da033")
@@ -234,21 +223,10 @@ mod tests {
let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap();
let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
let amount = BigUint::from(1000u64);
let approvals =
vec![Approval { owner, spender, token: token.clone(), amount: amount.clone() }];
let encoded_approvals = permit2
.encode_approvals(approvals)
let (permit, _) = permit2
.get_permit(&spender, &owner, &token, &amount)
.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 {
token: bytes_to_address(&token).unwrap(),
@@ -263,7 +241,7 @@ mod tests {
};
assert_eq!(
decoded_permit_single, expected_permit_single,
permit, expected_permit_single,
"Decoded PermitSingle does not match expected values"
);
}
@@ -309,18 +287,13 @@ mod tests {
assert!(receipt.status(), "Approve transaction failed");
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
.encode_approvals(approvals)
let (permit, signature) = permit2
.get_permit(&spender, &anvil_account, &token, &amount)
.unwrap();
let encoded = &encoded_approvals[0];
let encoded =
(bytes_to_address(&anvil_account).unwrap(), permit, signature.as_bytes().to_vec())
.abi_encode();
let function_signature =
"permit(address,((address,uint160,uint48,uint48),address,uint256),bytes)";

View File

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

View File

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