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 39af674
Show file tree
Hide file tree
Showing 7 changed files with 438 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
283 changes: 189 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::{U256, ExecutionInfo};
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,140 @@ 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 @@ -156,6 +302,7 @@ impl<P: Provider + Sync + Send + 'static + std::fmt::Debug> KatanaHooker for Sol
&self,
transaction: BroadcastedInvokeTransaction,
) -> bool {

let calls = match Vec::<TxCall>::cairo_deserialize(&transaction.calldata, 0) {
Ok(calls) => calls,
Err(e) => {
Expand All @@ -169,96 +316,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 +330,45 @@ 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 39af674

Please sign in to comment.