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:
@@ -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,59 +107,48 @@ 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)?;
|
||||||
|
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 details = PermitDetails { token: bytes_to_address(token)?, amount, expiration, nonce };
|
||||||
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 {
|
let permit_single = PermitSingle {
|
||||||
token: bytes_to_address(&approval.token)?,
|
details,
|
||||||
amount,
|
spender: bytes_to_address(spender)?,
|
||||||
expiration,
|
sigDeadline: sig_deadline,
|
||||||
nonce,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let permit_single = PermitSingle {
|
let domain = eip712_domain! {
|
||||||
details,
|
name: "Permit2",
|
||||||
spender: bytes_to_address(&approval.spender)?,
|
chain_id: self.chain_id,
|
||||||
sigDeadline: sig_deadline,
|
verifying_contract: self.address,
|
||||||
};
|
};
|
||||||
|
let hash = permit_single.eip712_signing_hash(&domain);
|
||||||
let domain = eip712_domain! {
|
let signature = self
|
||||||
name: "Permit2",
|
.signer
|
||||||
chain_id: self.chain_id,
|
.sign_hash_sync(&hash)
|
||||||
verifying_contract: self.address,
|
.map_err(|e| {
|
||||||
};
|
EncodingError::FatalError(format!(
|
||||||
let hash = permit_single.eip712_signing_hash(&domain);
|
"Failed to sign permit2 approval with error: {}",
|
||||||
let signature = self
|
e
|
||||||
.signer
|
))
|
||||||
.sign_hash_sync(&hash)
|
})?;
|
||||||
.map_err(|e| {
|
Ok((permit_single, signature))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)";
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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>;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user