Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub const INVALID_ZERO_TOKEN: felt252 = 'INVALID_ZERO_TOKEN';
pub const ORDER_EXPIRED: felt252 = 'ORDER_EXPIRED';
pub const TOKEN_ALREADY_REGISTERED: felt252 = 'TOKEN_ALREADY_REGISTERED';
pub const TOKEN_NOT_REGISTERED: felt252 = 'TOKEN_NOT_REGISTERED';
pub const UNALLOWED_ADDRESS: felt252 = 'UNALLOWED_ADDRESS';
pub const UNAPPROVED_COUNTERPARTY: felt252 = 'UNAPPROVED_COUNTERPARTY';

pub fn transfer_failed_error(
token: ContractAddress, sender: ContractAddress, amount: u128,
Expand Down
21 changes: 14 additions & 7 deletions src/order.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ pub struct Order {
pub sell_amount: u128,
pub buy_amount: u128,
// Addresses the user is willing to trade with. Empty span means any address.
pub allowed_addresses: Span<ContractAddress>,
pub approved_counterparties: Span<ContractAddress>,
}

pub impl HashOrderImpl<S, +HashStateTrait<S>, +Drop<S>> of Hash<Order, S> {
fn update_state(mut state: S, value: Order) -> S {
let Order {
salt, expiry, user, sell_token, buy_token, sell_amount, buy_amount, allowed_addresses,
salt,
expiry,
user,
sell_token,
buy_token,
sell_amount,
buy_amount,
approved_counterparties,
} = value;
state = state
.update_with(salt)
Expand All @@ -31,8 +38,8 @@ pub impl HashOrderImpl<S, +HashStateTrait<S>, +Drop<S>> of Hash<Order, S> {
.update_with(buy_token)
.update_with(sell_amount)
.update_with(buy_amount)
.update_with(allowed_addresses.len());
for elem in allowed_addresses {
.update_with(approved_counterparties.len());
for elem in approved_counterparties {
state = state.update_with(*elem);
}

Expand All @@ -50,14 +57,14 @@ pub impl HashOrderImpl<S, +HashStateTrait<S>, +Drop<S>> of Hash<Order, S> {
/// \"buy_token\":\"ContractAddress\",
/// \"sell_amount\":\"u128\",
/// \"buy_amount\":\"u128\",
/// \"allowed_addresses\":\"Span<ContractAddress>\"
/// \"approved_counterparties\":\"Span<ContractAddress>\"
/// )
/// \"Timestamp\"(
/// \"seconds\":\"u64\"
/// )
/// );

const ORDER_TYPE_HASH: HashType = 0x2d9dc4d67a6cc048d96cd39c037575fa17fc4f4ba67fa9307cd08d3aac3943b;
const ORDER_TYPE_HASH: HashType = 0x211c45dbc2e66ee156228a18612613a79470506142cea568b05e981a74efbb;

impl StructHashImpl of StructHash<Order> {
fn hash_struct(self: @Order) -> HashType {
Expand All @@ -74,7 +81,7 @@ mod tests {
#[test]
fn test_order_type_hash() {
let expected = selector!(
"\"Order\"(\"salt\":\"felt\",\"expiry\":\"Timestamp\",\"user\":\"ContractAddress\",\"sell_token\":\"ContractAddress\",\"buy_token\":\"ContractAddress\",\"sell_amount\":\"u128\",\"buy_amount\":\"u128\",\"allowed_addresses\":\"Span<ContractAddress>\")\"Timestamp\"(\"seconds\":\"u64\")",
"\"Order\"(\"salt\":\"felt\",\"expiry\":\"Timestamp\",\"user\":\"ContractAddress\",\"sell_token\":\"ContractAddress\",\"buy_token\":\"ContractAddress\",\"sell_amount\":\"u128\",\"buy_amount\":\"u128\",\"approved_counterparties\":\"Span<ContractAddress>\")\"Timestamp\"(\"seconds\":\"u64\")",
);
assert_eq!(ORDER_TYPE_HASH.into_base_16_string(), expected.into_base_16_string());
}
Expand Down
17 changes: 12 additions & 5 deletions src/payments.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ pub mod payments {
INVALID_AMOUNT_RATIO, INVALID_AMOUNT_TOO_LARGE, INVALID_DOWNCAST_AFTER_DIVISION,
INVALID_HIGH_FEE, INVALID_HIGH_FEE_LIMIT, INVALID_TOKEN_PAIR, INVALID_TRADE_SAME_USER,
INVALID_ZERO_ADDRESS, INVALID_ZERO_AMOUNT, INVALID_ZERO_TOKEN, ORDER_EXPIRED,
TOKEN_ALREADY_REGISTERED, TOKEN_NOT_REGISTERED, UNALLOWED_ADDRESS, transfer_failed_error,
TOKEN_ALREADY_REGISTERED, TOKEN_NOT_REGISTERED, UNAPPROVED_COUNTERPARTY,
transfer_failed_error,
};
use crate::events::{
FeeRecipientSet, FeeSet, OrderCanceled, TokenRegistered, TokenRemoved, TradeExecuted,
};
use crate::interface::IPayments;
use crate::order::Order;
use crate::utils::{is_allowed_address, validate_signature};
use crate::utils::{is_approved_counterparty, validate_signature};

component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);
component!(path: PausableComponent, storage: pausable, event: PausableEvent);
Expand Down Expand Up @@ -435,9 +436,15 @@ pub mod payments {
assert(order_a_actual_sell_amount.is_non_zero(), INVALID_ZERO_AMOUNT);
assert(order_a_actual_buy_amount.is_non_zero(), INVALID_ZERO_AMOUNT);

// Validate allowed addresses.
assert(is_allowed_address(order_b.user, order_a.allowed_addresses), UNALLOWED_ADDRESS);
assert(is_allowed_address(order_a.user, order_b.allowed_addresses), UNALLOWED_ADDRESS);
// Validate approved counterparties.
assert(
is_approved_counterparty(order_b.user, order_a.approved_counterparties),
UNAPPROVED_COUNTERPARTY,
);
assert(
is_approved_counterparty(order_a.user, order_b.approved_counterparties),
UNAPPROVED_COUNTERPARTY,
);
}


Expand Down
16 changes: 10 additions & 6 deletions src/tests/test_payments.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::num::traits::Zero;
use openzeppelin::utils::snip12::OffchainMessageHash;
use snforge_std::cheatcodes::events::{EventSpyTrait, EventsFilterTrait};
use snforge_std::{map_entry_address, store};
use starknet::ContractAddress;
Expand All @@ -7,6 +8,7 @@ use starknet_payments::interface::{
IPaymentsDispatcher, IPaymentsDispatcherTrait, IPaymentsSafeDispatcher,
IPaymentsSafeDispatcherTrait,
};
use starkware_utils::signature::stark::HashType;
use starkware_utils::time::time::Timestamp;
use starkware_utils_testing::constants as testing_constants;
use starkware_utils_testing::test_utils::{
Expand All @@ -15,6 +17,7 @@ use starkware_utils_testing::test_utils::{
};
use crate::events;
use crate::order::Order;
use crate::payments::payments::SNIP12MetadataImpl;
use crate::tests::test_utils::*;

fn default_order() -> Order {
Expand All @@ -26,7 +29,7 @@ fn default_order() -> Order {
buy_token: testing_constants::DUMMY_ADDRESS,
sell_amount: 100,
buy_amount: 200,
allowed_addresses: array![].span(),
approved_counterparties: array![].span(),
}
}

Expand Down Expand Up @@ -169,17 +172,18 @@ fn test_failed_set_fee() {
fn test_successful_handle_order() {
let contract_address = init_contract_with_roles();
let dispatcher = IPaymentsDispatcher { contract_address };
let user = testing_constants::DUMMY_ADDRESS;
let mut spy = snforge_std::spy_events();

let order_1 = Order { sell_amount: 10, ..default_order() };
let order_2 = Order { salt: 2, sell_amount: 20, ..default_order() };
let order_3 = Order { salt: 3, sell_amount: 30, ..default_order() };
let orders = array![order_1, order_2, order_3];
let order_hashes = array![
3250832918082879608022746380123673061315069847566627860201489924189339339467,
3259641975931468454375849282716321416060344786234492516391729290818542841802,
3472711217305392937857639177600091754979974757056915903236482173645832579133,
];
let mut order_hashes: Array<HashType> = array![];
for order in orders.span() {
let message_hash = order.get_message_hash(user);
order_hashes.append(message_hash);
}

for order_hash in order_hashes.span() {
assert_eq!(dispatcher.get_order_fulfillment(*order_hash), 0);
Expand Down
10 changes: 5 additions & 5 deletions src/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ fn is_in_span(tested_address: ContractAddress, address_list: Span<ContractAddres
false
}

pub fn is_allowed_address(
tested_address: ContractAddress, allowed_addresses: Span<ContractAddress>,
pub fn is_approved_counterparty(
tested_address: ContractAddress, approved_counterparties: Span<ContractAddress>,
) -> bool {
// Means all addresses are allowed.
if allowed_addresses.len() == 0 {
// Means all addresses are approved.
if approved_counterparties.len() == 0 {
return true;
}

is_in_span(tested_address, allowed_addresses)
is_in_span(tested_address, approved_counterparties)
}

pub fn validate_signature(signer: ContractAddress, hash: felt252, signature: Span<felt252>) {
Expand Down