Skip to content

Commit

Permalink
Hooker: add verification ownership & balance
Browse files Browse the repository at this point in the history
  • Loading branch information
ybensacq committed Mar 8, 2024
1 parent c528bcd commit e85b414
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 98 deletions.
46 changes: 46 additions & 0 deletions artifacts/starknet_utils.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@
[
{
"type": "struct",
"name": "package_name::ExecutionInfo",
"members": [
{
"name": "order_hash",
"type": "core::felt252"
},
{
"name": "nft_address",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "nft_from",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "nft_to",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "nft_token_id",
"type": "core::integer::u256"
},
{
"name": "payment_from",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "payment_to",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "payment_amount",
"type": "core::integer::u256"
},
{
"name": "payment_currency_address",
"type": "core::starknet::contract_address::ContractAddress"
},
{
"name": "payment_currency_chain_id",
"type": "core::felt252"
}
]
},
{
"type": "struct",
"name": "core::integer::u256",
Expand Down
292 changes: 198 additions & 94 deletions crates/solis/src/hooker.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Solis hooker on Katana transaction lifecycle.
//!
use crate::contracts::starknet_utils::U256;
use crate::contracts::starknet_utils::{ExecutionInfo, U256};
use async_trait::async_trait;
use cainome::cairo_serde::CairoSerde;
use cainome::rs::abigen;
Expand All @@ -21,6 +21,18 @@ use crate::contracts::orderbook::{OrderV1, RouteType};
use crate::contracts::starknet_utils::StarknetUtilsReader;
use crate::CHAIN_ID_SOLIS;

struct OwnershipVerifier {
token_address: ContractAddress,
token_id: U256,
current_owner: cainome::cairo_serde::ContractAddress,
}

struct BalanceVerifier {
currency_address: ContractAddress,
offerer: cainome::cairo_serde::ContractAddress,
start_amount: U256,
}

abigen!(CallContract, "./artifacts/contract.abi.json");

/// Hooker struct, with already instanciated contracts/readers
Expand All @@ -36,6 +48,139 @@ pub struct SolisHooker<P: Provider + Sync + Send + 'static + std::fmt::Debug> {
sequencer: Option<Arc<KatanaSequencer>>,
}

impl<P: Provider + Sync + Send + 'static + std::fmt::Debug> SolisHooker<P> {
/// Verify the ownership of a token
async fn verify_ownership(&self, ownership_verifier: &OwnershipVerifier) -> bool {
let sn_utils_reader_nft_address = StarknetUtilsReader::new(
ownership_verifier.token_address.into(),
self.sn_utils_reader.provider(),
);

// check the current owner of the token.
let owner = sn_utils_reader_nft_address
.ownerOf(&ownership_verifier.token_id)
.call()
.await;

if let Ok(owner_address) = owner {
if owner_address != ownership_verifier.current_owner {
tracing::trace!(
"\nOwner {:?} differs from offerer {:?} ",
owner,
ownership_verifier.current_owner
);

println!(
"\nOwner {:?} differs from offerer {:?} ",
owner, ownership_verifier.current_owner
);

return false;
}
}

true
}

async fn verify_balance(&self, balance_verifier: &BalanceVerifier) -> bool {
let sn_utils_reader_erc20_address = StarknetUtilsReader::new(
balance_verifier.currency_address.into(),
self.sn_utils_reader.provider(),
);
let allowance = sn_utils_reader_erc20_address
.allowance(&balance_verifier.offerer, &self.sn_executor_address.into())
.call()
.await;

if let Ok(allowance) = allowance {
if allowance < balance_verifier.start_amount {
tracing::trace!(
"\nAllowance {:?} is not enough {:?} ",
allowance,
balance_verifier.start_amount
);
println!(
"\nAllowance {:?} is not enough {:?} ",
allowance, balance_verifier.start_amount
);
return false;
}
}

// check the balance
let balance = sn_utils_reader_erc20_address
.balanceOf(&balance_verifier.offerer)
.call()
.await;
if let Ok(balance) = balance {
if balance < balance_verifier.start_amount {
tracing::trace!(
"\nBalance {:?} is not enough {:?} ",
balance,
balance_verifier.start_amount
);
println!(
"\nBalance {:?} is not enough {:?} ",
balance, balance_verifier.start_amount
);
return false;
}
}

true
}

async fn verify_call(&self, call: &TxCall) -> bool {
let order = match OrderV1::cairo_deserialize(&call.calldata, 0) {
Ok(order) => order,
Err(e) => {
tracing::error!("Fail deserializing OrderV1: {:?}", e);
return false;
}
};

// ERC721 to ERC20
if order.route == RouteType::Erc721ToErc20 {
let token_id = order.token_id.clone().unwrap();
let n_token_id = U256 {
low: token_id.low,
high: token_id.high,
};

let verifier = OwnershipVerifier {
token_address: ContractAddress(order.token_address.into()),
token_id: n_token_id,
current_owner: cainome::cairo_serde::ContractAddress(order.offerer.into()),
};

let owner_ship_verification = self.verify_ownership(&verifier).await;
if !owner_ship_verification {
println!("verify ownership for starknet before failed");
return false;
}
}

// ERC20 to ERC721 : we check the allowance and the offerer balance.
if order.route == RouteType::Erc20ToErc721 {
if !self
.verify_balance(&BalanceVerifier {
currency_address: ContractAddress(order.currency_address.into()),
offerer: cainome::cairo_serde::ContractAddress(order.offerer.into()),
start_amount: U256 {
low: order.start_amount.low,
high: order.start_amount.high,
},
})
.await
{
println!("verify balance for starknet before failed");
return false;
}
}
return true;
}
}

impl<P: Provider + Sync + Send + 'static + std::fmt::Debug> SolisHooker<P> {
/// Initializes a new instance.
pub fn new(
Expand Down Expand Up @@ -169,96 +314,8 @@ impl<P: Provider + Sync + Send + 'static + std::fmt::Debug> KatanaHooker for Sol
continue;
}

let order = match OrderV1::cairo_deserialize(&call.calldata, 0) {
Ok(order) => order,
Err(e) => {
tracing::error!("Fail deserializing OrderV1: {:?}", e);
return false;
}
};

let sn_utils_reader_nft_address = StarknetUtilsReader::new(
order.token_address.into(),
self.sn_utils_reader.provider(),
);

// ERC721 to ERC20
if order.route == RouteType::Erc721ToErc20 {
let token_id = order.token_id.clone().unwrap();
let n_token_id = U256 {
low: token_id.low,
high: token_id.high,
};

// check the current owner of the token.
let owner = sn_utils_reader_nft_address
.ownerOf(&n_token_id)
.call()
.await;
if let Ok(owner_address) = owner {
if owner_address != order.offerer {
tracing::trace!(
"\nOwner {:?} differs from offerer {:?} ",
owner,
order.offerer
);
return false;
}
}
}

// ERC20 to ERC721 : we check the allowance and the offerer balance.
if order.route == RouteType::Erc20ToErc721 {
let sn_utils_reader_erc20_address = StarknetUtilsReader::new(
order.currency_address.into(),
self.sn_utils_reader.provider(),
);
let allowance = sn_utils_reader_erc20_address
.allowance(&order.offerer, &self.sn_executor_address.into())
.call()
.await;
if let Ok(allowance) = allowance {
let n_start_amount = U256 {
low: order.start_amount.low,
high: order.start_amount.high,
};
if allowance < n_start_amount {
tracing::trace!(
"\nAllowance {:?} is not enough {:?} ",
allowance,
order.start_amount
);
println!(
"\nAllowance {:?} is not enough {:?} ",
allowance, order.start_amount
);
return false;
}
}

// check the balance
let balance = sn_utils_reader_erc20_address
.balanceOf(&order.offerer)
.call()
.await;
if let Ok(balance) = balance {
let n_start_amount = U256 {
low: order.start_amount.low,
high: order.start_amount.high,
};
if balance < n_start_amount {
tracing::trace!(
"\nBalance {:?} is not enough {:?} ",
balance,
order.start_amount
);
println!(
"\nBalance {:?} is not enough {:?} ",
balance, order.start_amount
);
return false;
}
}
if !self.verify_call(&call).await {
return false;
}

// TODO: check assets on starknet.
Expand All @@ -271,9 +328,56 @@ impl<P: Provider + Sync + Send + 'static + std::fmt::Debug> KatanaHooker for Sol
async fn verify_tx_for_starknet(&self, call: Call) -> bool {
println!("verify message to starknet before tx: {:?}", call);

// TODO: Decode the ExecutionInfo from the calldata.
// Check that assets are still in the good location. When this function
// returns true, a transaction is fired on Starknet for the execution.
let execution_info = match ExecutionInfo::cairo_deserialize(&call.calldata, 0) {
Ok(execution_info) => execution_info,
Err(e) => {
tracing::error!("Fail deserializing ExecutionInfo: {:?}", e);
return false;
}
};

println!(
"verify balance for starknet before from : {:?}",
execution_info.nft_from
);
println!(
"verify balance for starknet before to : {:?}",
execution_info.nft_to
);
println!(
"verify balance for starknet before amount : {:?}",
execution_info.payment_amount.low
);

let verifier = OwnershipVerifier {
token_address: ContractAddress(execution_info.nft_address.into()),
token_id: execution_info.nft_token_id,
current_owner: cainome::cairo_serde::ContractAddress(execution_info.nft_to.into()),
};

let owner_ship_verification = self.verify_ownership(&verifier).await;
if !owner_ship_verification {
println!("verify ownership for starknet before failed");
return false;
}

if !self
.verify_balance(&BalanceVerifier {
currency_address: ContractAddress(execution_info.payment_currency_address.into()),
offerer: cainome::cairo_serde::ContractAddress(execution_info.nft_from.into()),
start_amount: U256 {
low: execution_info.payment_amount.low,
high: execution_info.payment_amount.high,
},
})
.await
{
return false;
}

println!("order nft_address{:?}", &execution_info.nft_address);
println!("order nft_from{:?}", &execution_info.nft_from);

true
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ STARKNET_ACCOUNT2_ADDRESS=0x5686a647a9cdd63ade617e0baf3b364856b813b508f03903eb58
STARKNET_ACCOUNT2_PRIVATE_KEY=0x33003003001800009900180300d206308b0070db00121318d17b5e6262150b
STARKNET_ACCOUNT2_PUBLIC_KEY=0x4c0f884b8e5b4f00d97a3aad26b2e5de0c0c76a555060c837da2e287403c01d

SOLIS_ADMIN_ADDRESS_DEV=0xb3ff441a68610b30fd5e2abbf3a1548eb6ba6f3559f2862bf2dc757e5828ca
SOLIS_ADMIN_PRIVATE_KEY_DEV=0x2bbf4f9fd0bbb2e60b0316c1fe0b76cf7a4d0198bd493ced9b8df2a3a24d68a
SOLIS_ADMIN_PUBLIC_KEY_DEV=0x640466ebd2ce505209d3e5c4494b4276ed8f1cde764d757eb48831961f7cdea

# Starknet network
STARKNET_NETWORK_ID=dev
# Solis network
Expand Down
Loading

0 comments on commit e85b414

Please sign in to comment.