From f083deb0069b901b3152de4faf3e1a7a3fe6d141 Mon Sep 17 00:00:00 2001
From: ptisserand
Date: Tue, 10 Sep 2024 09:59:28 +0200
Subject: [PATCH] feat(contract): explore the possibility to use a fully
onchain orderbook on (#439)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description
1. Orderbook is now a component
2. Remove messaging in Starknet part
3. Add `version` and `order_type` in Orderbook component events
4. Move dependencies version to workspace Scarb.toml
5. Update to cairo 2.7.1, snforge 0.28 & OZ 0.15.1
## What type of PR is this? (check all applicable)
- [x] 🍕 Feature (`feat:`)
- [ ] 🐛 Bug Fix (`fix:`)
- [ ] 📝 Documentation Update (`docs:`)
- [ ] 🎨 Style (`style:`)
- [ ] 🧑💻 Code Refactor (`refactor:`)
- [ ] 🔥 Performance Improvements (`perf:`)
- [ ] ✅ Test (`test:`)
- [ ] 🤖 Build (`build:`)
- [ ] 🔁 CI (`ci:`)
- [ ] 📦 Chore (`chore:`)
- [ ] ⏩ Revert (`revert:`)
- [ ] 🚀 Breaking Changes (`BREAKING CHANGE:`)
## Related Tickets & Documents
## Added tests?
- [ ] 👍 yes
- [ ] 🙅 no, because they aren't needed
- [ ] 🙋 no, because I need help
## Added to documentation?
- [ ] 📜 README.md
- [ ] 📓 Documentation
- [ ] 🙅 no documentation needed
## [optional] Are there any post-deployment tasks we need to perform?
## [optional] What gif best describes this PR or how it makes you feel?
### PR Title and Description Guidelines:
- Ensure your PR title follows semantic versioning standards. This helps
automate releases and changelogs.
- Use types like `feat:`, `fix:`, `chore:`, `BREAKING CHANGE:` etc. in
your PR title.
- Your PR title will be used as a commit message when merging. Make sure
it adheres to [Conventional Commits
standards](https://www.conventionalcommits.org/).
## Closing Issues
---
.github/workflows/arkproject-contracts.yml | 6 +-
contracts/.tool-versions | 4 +-
contracts/Scarb.lock | 107 ++-
contracts/Scarb.toml | 18 +-
contracts/ark_common/Scarb.toml | 9 +-
contracts/ark_common/src/crypto/common.cairo | 3 +-
.../ark_common/src/crypto/typed_data.cairo | 2 +-
contracts/ark_common/src/lib.cairo | 6 +-
.../src/protocol/order_database.cairo | 5 +-
.../ark_common/src/protocol/order_types.cairo | 4 +-
.../ark_common/src/protocol/order_v1.cairo | 10 +-
.../ark_common/tests/test_type_data.cairo | 10 +-
contracts/ark_component/Scarb.toml | 19 +
contracts/ark_component/src/lib.cairo | 1 +
contracts/ark_component/src/orderbook.cairo | 9 +
.../src/orderbook/interface.cairo | 80 ++
.../src/orderbook/orderbook.cairo | 829 ++++++++++++++++++
contracts/ark_orderbook/Scarb.toml | 11 +-
contracts/ark_orderbook/src/orderbook.cairo | 775 +++-------------
.../src/orderbook_event_mock.cairo | 8 +-
.../ark_orderbook/tests/common/setup.cairo | 13 +-
contracts/ark_orderbook/tests/lib.cairo | 2 +-
.../tests/unit/order/test_order_v1.cairo | 18 +-
.../tests/unit/test_orderbook.cairo | 139 +--
contracts/ark_oz/Scarb.lock | 96 +-
contracts/ark_oz/Scarb.toml | 12 +-
contracts/ark_oz/src/erc2981/erc2981.cairo | 6 +-
contracts/ark_oz/tests/test_erc2981.cairo | 48 +-
contracts/ark_starknet/Scarb.toml | 14 +-
.../ark_starknet/src/appchain_messaging.cairo | 7 +-
contracts/ark_starknet/src/executor.cairo | 361 ++++----
contracts/ark_starknet/src/interfaces.cairo | 6 +-
contracts/ark_starknet/src/lib.cairo | 2 +-
.../ark_starknet/tests/common/setup.cairo | 30 +-
.../tests/integration/create_order.cairo | 44 +-
.../tests/integration/execute_order.cairo | 119 ++-
.../tests/integration/fees_amount.cairo | 25 +-
.../tests/integration/fulfill_order.cairo | 163 ++--
.../tests/integration/maintenance.cairo | 28 +-
contracts/ark_starknet/tests/lib.cairo | 2 +-
.../ark_starknet/tests/unit/test_fees.cairo | 21 +-
contracts/ark_tokens/Scarb.toml | 11 +-
contracts/ark_tokens/src/erc20.cairo | 3 +-
contracts/ark_tokens/src/erc721.cairo | 11 +-
contracts/ark_tokens/src/erc721_royalty.cairo | 16 +-
contracts/ark_tokens/src/lib.cairo | 2 +-
contracts/solis/Scarb.toml | 3 +
47 files changed, 1811 insertions(+), 1307 deletions(-)
create mode 100644 contracts/ark_component/Scarb.toml
create mode 100644 contracts/ark_component/src/lib.cairo
create mode 100644 contracts/ark_component/src/orderbook.cairo
create mode 100644 contracts/ark_component/src/orderbook/interface.cairo
create mode 100644 contracts/ark_component/src/orderbook/orderbook.cairo
diff --git a/.github/workflows/arkproject-contracts.yml b/.github/workflows/arkproject-contracts.yml
index 99bf305e3..499894f3c 100644
--- a/.github/workflows/arkproject-contracts.yml
+++ b/.github/workflows/arkproject-contracts.yml
@@ -22,7 +22,7 @@ jobs:
- name: Setup Scarb
uses: software-mansion/setup-scarb@v1
with:
- scarb-version: 2.5.4
+ scarb-version: 2.7.1
- name: Check Scarb Formatting
run: cd contracts && scarb fmt --check
test:
@@ -36,12 +36,12 @@ jobs:
- name: Setup Scarb
uses: software-mansion/setup-scarb@v1
with:
- scarb-version: 2.5.4
+ scarb-version: 2.7.1
- name: Setup Starknet Foundry
uses: foundry-rs/setup-snfoundry@v2
with:
- starknet-foundry-version: 0.18.0
+ starknet-foundry-version: 0.28.0
- name: Test ark_common contracts
run: cd contracts/ark_common && snforge test
diff --git a/contracts/.tool-versions b/contracts/.tool-versions
index 90faff46b..e0955bfc3 100644
--- a/contracts/.tool-versions
+++ b/contracts/.tool-versions
@@ -1,2 +1,2 @@
-scarb 2.5.4
-starknet-foundry 0.18.0
\ No newline at end of file
+scarb 2.7.1
+starknet-foundry 0.28.0
\ No newline at end of file
diff --git a/contracts/Scarb.lock b/contracts/Scarb.lock
index c8865abf6..0c022abc2 100644
--- a/contracts/Scarb.lock
+++ b/contracts/Scarb.lock
@@ -8,11 +8,20 @@ dependencies = [
"snforge_std",
]
+[[package]]
+name = "ark_component"
+version = "0.1.0"
+dependencies = [
+ "ark_common",
+ "snforge_std",
+]
+
[[package]]
name = "ark_orderbook"
version = "0.1.0"
dependencies = [
"ark_common",
+ "ark_component",
"snforge_std",
]
@@ -21,7 +30,6 @@ name = "ark_oz"
version = "0.1.0"
dependencies = [
"openzeppelin",
- "snforge_std",
]
[[package]]
@@ -29,6 +37,7 @@ name = "ark_starknet"
version = "0.1.0"
dependencies = [
"ark_common",
+ "ark_component",
"ark_oz",
"ark_tokens",
"openzeppelin",
@@ -46,13 +55,101 @@ dependencies = [
[[package]]
name = "openzeppelin"
-version = "0.10.0"
-source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.10.0#d77082732daab2690ba50742ea41080eb23299d3"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_access",
+ "openzeppelin_account",
+ "openzeppelin_governance",
+ "openzeppelin_introspection",
+ "openzeppelin_presets",
+ "openzeppelin_security",
+ "openzeppelin_token",
+ "openzeppelin_upgrades",
+ "openzeppelin_utils",
+]
+
+[[package]]
+name = "openzeppelin_access"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_introspection",
+ "openzeppelin_utils",
+]
+
+[[package]]
+name = "openzeppelin_account"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_introspection",
+ "openzeppelin_utils",
+]
+
+[[package]]
+name = "openzeppelin_governance"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_access",
+ "openzeppelin_introspection",
+]
+
+[[package]]
+name = "openzeppelin_introspection"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "openzeppelin_presets"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_access",
+ "openzeppelin_account",
+ "openzeppelin_introspection",
+ "openzeppelin_token",
+ "openzeppelin_upgrades",
+]
+
+[[package]]
+name = "openzeppelin_security"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "openzeppelin_token"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_account",
+ "openzeppelin_governance",
+ "openzeppelin_introspection",
+]
+
+[[package]]
+name = "openzeppelin_upgrades"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "openzeppelin_utils"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "snforge_scarb_plugin"
+version = "0.1.0"
+source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.28.0#4dfe39d96690ed6b3d56971512700de3f58288ea"
[[package]]
name = "snforge_std"
-version = "0.18.0"
-source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.18.0#48f909a56b08cbdc5ca6a21a836b0fbc6c36d55b"
+version = "0.28.0"
+source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.28.0#4dfe39d96690ed6b3d56971512700de3f58288ea"
+dependencies = [
+ "snforge_scarb_plugin",
+]
[[package]]
name = "solis"
diff --git a/contracts/Scarb.toml b/contracts/Scarb.toml
index 83a37c9de..f9b906048 100644
--- a/contracts/Scarb.toml
+++ b/contracts/Scarb.toml
@@ -1,6 +1,22 @@
[workspace]
-members = ["ark_common", "ark_orderbook", "ark_starknet", "ark_tokens", "solis"]
+members = [
+ "ark_common",
+ "ark_component",
+ "ark_orderbook",
+ "ark_starknet",
+ "ark_tokens",
+ "solis",
+]
+
+[workspace.dependencies]
+starknet = "2.7.1"
+openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.1" }
+snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" }
+assert_macros = "0.1.0"
[workspace.scripts]
test = "snforge test"
+
+[workspace.tool.fmt]
+sort-module-level-items = true
diff --git a/contracts/ark_common/Scarb.toml b/contracts/ark_common/Scarb.toml
index 3dca0b95c..d7fa37b83 100644
--- a/contracts/ark_common/Scarb.toml
+++ b/contracts/ark_common/Scarb.toml
@@ -5,8 +5,13 @@ version = "0.1.0"
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
[dependencies]
-starknet = "2.5.4"
-snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.18.0" }
+starknet.workspace = true
+
+[dev-dependencies]
+snforge_std.workspace = true
+
+[tool]
+fmt.workspace = true
[lib]
diff --git a/contracts/ark_common/src/crypto/common.cairo b/contracts/ark_common/src/crypto/common.cairo
index 8908a61a2..61cf5b5d3 100644
--- a/contracts/ark_common/src/crypto/common.cairo
+++ b/contracts/ark_common/src/crypto/common.cairo
@@ -1,7 +1,6 @@
-use traits::Into;
-
// locals
use super::constants;
+use traits::Into;
fn hash_u256(n: u256) -> felt252 {
let mut hash = pedersen::pedersen(0, constants::U256_TYPE_HASH);
diff --git a/contracts/ark_common/src/crypto/typed_data.cairo b/contracts/ark_common/src/crypto/typed_data.cairo
index 1d5389806..c5f24c3a6 100644
--- a/contracts/ark_common/src/crypto/typed_data.cairo
+++ b/contracts/ark_common/src/crypto/typed_data.cairo
@@ -1,6 +1,6 @@
-use traits::Into;
use box::BoxTrait;
use super::constants;
+use traits::Into;
const ORDER_TYPE_HASH: felt252 = 0x3749634CC837ADA5AF76B97FC3197F05DFC376CF4B73199E76107754E39CA1D;
diff --git a/contracts/ark_common/src/lib.cairo b/contracts/ark_common/src/lib.cairo
index 54eb951bc..8650199ab 100644
--- a/contracts/ark_common/src/lib.cairo
+++ b/contracts/ark_common/src/lib.cairo
@@ -1,13 +1,13 @@
mod protocol {
+ mod order_database;
mod order_types;
mod order_v1;
- mod order_database;
}
mod crypto {
+ mod common;
+ mod constants;
mod hash;
mod signer;
mod typed_data;
- mod constants;
- mod common;
}
diff --git a/contracts/ark_common/src/protocol/order_database.cairo b/contracts/ark_common/src/protocol/order_database.cairo
index 03edefa30..9c71a485f 100644
--- a/contracts/ark_common/src/protocol/order_database.cairo
+++ b/contracts/ark_common/src/protocol/order_database.cairo
@@ -1,3 +1,5 @@
+use ark_common::protocol::order_types::OrderStatus;
+use ark_common::protocol::order_types::OrderType;
use core::result::ResultTrait;
//! Orders database.
//!
@@ -20,9 +22,6 @@ use core::result::ResultTrait;
//! 4. At base addresss + offset 2 => First felt of the serialized order.
use starknet::SyscallResultTrait;
-use ark_common::protocol::order_types::OrderStatus;
-use ark_common::protocol::order_types::OrderType;
-
/// Must remain equal to 0 for now.
const ADDRESS_DOMAIN: u32 = 0;
/// A constant value used in the base key hash.
diff --git a/contracts/ark_common/src/protocol/order_types.cairo b/contracts/ark_common/src/protocol/order_types.cairo
index cd903c157..34331abc1 100644
--- a/contracts/ark_common/src/protocol/order_types.cairo
+++ b/contracts/ark_common/src/protocol/order_types.cairo
@@ -1,7 +1,7 @@
+use ark_common::protocol::order_v1::OrderV1;
+use poseidon::poseidon_hash_span;
//! Order generic variables.
use starknet::ContractAddress;
-use poseidon::poseidon_hash_span;
-use ark_common::protocol::order_v1::OrderV1;
/// Order types.
#[derive(Serde, Drop, PartialEq, Copy, Debug, starknet::Store)]
diff --git a/contracts/ark_common/src/protocol/order_v1.cairo b/contracts/ark_common/src/protocol/order_v1.cairo
index f20bce4dd..4fd202216 100644
--- a/contracts/ark_common/src/protocol/order_v1.cairo
+++ b/contracts/ark_common/src/protocol/order_v1.cairo
@@ -1,16 +1,16 @@
+use ark_common::protocol::order_types::FulfillInfo;
+use ark_common::protocol::order_types::{OrderTrait, OrderValidationError, OrderType, RouteType};
use core::array::ArrayTrait;
+use core::option::OptionTrait;
use core::traits::Into;
use core::traits::TryInto;
-use core::option::OptionTrait;
+use poseidon::poseidon_hash_span;
//! Order v1 supported by the Orderbook.
//!
use starknet::ContractAddress;
-use starknet::contract_address_to_felt252;
-use ark_common::protocol::order_types::{OrderTrait, OrderValidationError, OrderType, RouteType};
-use ark_common::protocol::order_types::FulfillInfo;
-use poseidon::poseidon_hash_span;
use starknet::SyscallResultTrait;
+use starknet::contract_address_to_felt252;
const ORDER_VERSION_V1: felt252 = 'v1';
// Auction -> end_amount (reserve price) > start_amount (starting price).
diff --git a/contracts/ark_common/tests/test_type_data.cairo b/contracts/ark_common/tests/test_type_data.cairo
index 7bacb6563..b4b8116b1 100644
--- a/contracts/ark_common/tests/test_type_data.cairo
+++ b/contracts/ark_common/tests/test_type_data.cairo
@@ -1,19 +1,19 @@
-use core::traits::TryInto;
-use traits::Into;
-use box::BoxTrait;
+use ark_common::crypto::signer::{Signer, SignInfo};
use ark_common::crypto::typed_data::{OrderSign, TypedDataTrait};
+use box::BoxTrait;
use core::option::OptionTrait;
-use ark_common::crypto::signer::{Signer, SignInfo};
+use core::traits::TryInto;
use snforge_std::signature::KeyPairTrait;
use snforge_std::signature::stark_curve::{
StarkCurveKeyPairImpl, StarkCurveSignerImpl, StarkCurveVerifierImpl
};
+use traits::Into;
fn sign_mock(message_hash: felt252) -> Signer {
let key_pair = KeyPairTrait::::generate();
- let (r, s): (felt252, felt252) = key_pair.sign(message_hash);
+ let (r, s): (felt252, felt252) = key_pair.sign(message_hash).unwrap();
Signer::WEIERSTRESS_STARKNET(
SignInfo { user_pubkey: key_pair.public_key, user_sig_r: r, user_sig_s: s, }
)
diff --git a/contracts/ark_component/Scarb.toml b/contracts/ark_component/Scarb.toml
new file mode 100644
index 000000000..4f922c2c0
--- /dev/null
+++ b/contracts/ark_component/Scarb.toml
@@ -0,0 +1,19 @@
+[package]
+name = "ark_component"
+version = "0.1.0"
+
+[dependencies]
+ark_common = { path = "../ark_common" }
+starknet.workspace = true
+
+[dev-dependencies]
+snforge_std.workspace = true
+assert_macros.workspace = true
+
+[scripts]
+test.workspace = true
+
+[tool]
+fmt.workspace = true
+
+[lib]
diff --git a/contracts/ark_component/src/lib.cairo b/contracts/ark_component/src/lib.cairo
new file mode 100644
index 000000000..959ef2db9
--- /dev/null
+++ b/contracts/ark_component/src/lib.cairo
@@ -0,0 +1 @@
+mod orderbook;
diff --git a/contracts/ark_component/src/orderbook.cairo b/contracts/ark_component/src/orderbook.cairo
new file mode 100644
index 000000000..191215137
--- /dev/null
+++ b/contracts/ark_component/src/orderbook.cairo
@@ -0,0 +1,9 @@
+pub mod interface;
+pub mod orderbook;
+pub use interface::{IOrderbook, IOrderbookAction, orderbook_errors};
+
+pub use orderbook::OrderbookComponent;
+pub use orderbook::{
+ OrderbookHooksCreateOrderEmptyImpl, OrderbookHooksCancelOrderEmptyImpl,
+ OrderbookHooksFulfillOrderEmptyImpl, OrderbookHooksValidateOrderExecutionEmptyImpl,
+};
diff --git a/contracts/ark_component/src/orderbook/interface.cairo b/contracts/ark_component/src/orderbook/interface.cairo
new file mode 100644
index 000000000..b8d6fa501
--- /dev/null
+++ b/contracts/ark_component/src/orderbook/interface.cairo
@@ -0,0 +1,80 @@
+use ark_common::protocol::order_types::{
+ CancelInfo, ExecutionInfo, ExecutionValidationInfo, FulfillInfo, OrderStatus, OrderType
+};
+use ark_common::protocol::order_v1::OrderV1;
+
+use starknet::ContractAddress;
+
+// *************************************************************************
+// ERRORS
+//
+// Error messages used within the orderbook contract.
+// *************************************************************************
+pub mod orderbook_errors {
+ const BROKER_UNREGISTERED: felt252 = 'OB: unregistered broker';
+ const ORDER_INVALID_DATA: felt252 = 'OB: order invalid data';
+ const ORDER_ALREADY_EXISTS: felt252 = 'OB: order already exists';
+ const ORDER_ALREADY_EXEC: felt252 = 'OB: order already executed';
+ const ORDER_NOT_FULFILLABLE: felt252 = 'OB: order not fulfillable';
+ const ORDER_NOT_FOUND: felt252 = 'OB: order not found';
+ const ORDER_FULFILLED: felt252 = 'OB: order fulfilled';
+ const ORDER_NOT_CANCELLABLE: felt252 = 'OB: order not cancellable';
+ const ORDER_EXPIRED: felt252 = 'OB: order expired';
+ const ORDER_SAME_OFFERER: felt252 = 'OB: order has same offerer';
+ const ORDER_NOT_SAME_OFFERER: felt252 = 'OB: fulfiller is not offerer';
+ const OFFER_ALREADY_EXISTS: felt252 = 'OB: offer already exists';
+ const ORDER_IS_EXPIRED: felt252 = 'OB: order is expired';
+ const ORDER_AUCTION_IS_EXPIRED: felt252 = 'OB: auction is expired';
+ const ORDER_MISSING_RELATED_ORDER: felt252 = 'OB: order missing related order';
+ const ORDER_HASH_DOES_NOT_MATCH: felt252 = 'OB: order hash does not match';
+ const ORDER_TOKEN_ID_DOES_NOT_MATCH: felt252 = 'OB: token id does not match';
+ const ORDER_TOKEN_ID_IS_MISSING: felt252 = 'OB: token id is missing';
+ const ORDER_TOKEN_HASH_DOES_NOT_MATCH: felt252 = 'OB: token hash does not match';
+ const ORDER_NOT_AN_OFFER: felt252 = 'OB: order is not an offer';
+ const ORDER_NOT_OPEN: felt252 = 'OB: order is not open';
+ const ORDER_OPEN: felt252 = 'OB: order is not open';
+ const USE_FULFILL_AUCTION: felt252 = 'OB: must use fulfill auction';
+ const OFFER_NOT_STARTED: felt252 = 'OB: offer is not started';
+ const INVALID_BROKER: felt252 = 'OB: broker is not whitelisted';
+}
+
+#[starknet::interface]
+pub trait IOrderbook {
+ /// Retrieves the type of an order using its hash.
+ ///
+ /// # Arguments
+ /// * `order_hash` - The order hash of order.
+ fn get_order_type(self: @T, order_hash: felt252) -> OrderType;
+
+ /// Retrieves the status of an order using its hash.
+ ///
+ /// # Arguments
+ /// * `order_hash` - The order hash of order.
+ fn get_order_status(self: @T, order_hash: felt252) -> OrderStatus;
+
+ /// Retrieves the auction end date.
+ ///
+ /// # Arguments
+ /// * `order_hash` - The order hash of order.
+ fn get_auction_expiration(self: @T, order_hash: felt252) -> u64;
+
+ /// Retrieves the order using its hash.
+ ///
+ /// # Arguments
+ /// * `order_hash` - The order hash of order.
+ fn get_order(self: @T, order_hash: felt252) -> OrderV1;
+
+ /// Retrieves the order hash using its token hash.
+ ///
+ /// # Arguments
+ /// * `token_hash` - The token hash of the order.
+ fn get_order_hash(self: @T, token_hash: felt252) -> felt252;
+}
+
+pub trait IOrderbookAction {
+ fn validate_order_execution(ref self: T, info: ExecutionValidationInfo);
+
+ fn create_order(ref self: T, order: OrderV1);
+ fn cancel_order(ref self: T, cancel_info: CancelInfo);
+ fn fulfill_order(ref self: T, fulfill_info: FulfillInfo) -> Option::;
+}
diff --git a/contracts/ark_component/src/orderbook/orderbook.cairo b/contracts/ark_component/src/orderbook/orderbook.cairo
new file mode 100644
index 000000000..c26a133a6
--- /dev/null
+++ b/contracts/ark_component/src/orderbook/orderbook.cairo
@@ -0,0 +1,829 @@
+#[starknet::component]
+pub mod OrderbookComponent {
+ use ark_common::protocol::order_database::{
+ order_read, order_status_read, order_write, order_status_write, order_type_read
+ };
+ use ark_common::protocol::order_types::{
+ OrderStatus, OrderTrait, OrderType, CancelInfo, FulfillInfo, ExecutionValidationInfo,
+ ExecutionInfo, RouteType
+ };
+ use ark_common::protocol::order_v1::OrderV1;
+ use core::debug::PrintTrait;
+ use core::option::OptionTrait;
+ use core::result::ResultTrait;
+ use core::starknet::event::EventEmitter;
+ use core::traits::Into;
+ use core::traits::TryInto;
+ use core::zeroable::Zeroable;
+ use starknet::ContractAddress;
+ use starknet::storage::Map;
+ use super::super::interface::{IOrderbook, IOrderbookAction, orderbook_errors};
+
+ const EXTENSION_TIME_IN_SECONDS: u64 = 600;
+ const AUCTION_ACCEPTING_TIME_SECS: u64 = 172800;
+ /// Storage struct for the Orderbook component.
+ #[storage]
+ struct Storage {
+ /// Mapping of broker addresses to their whitelisted status.
+ /// Represented as felt252, set to 1 if the broker is registered.
+ brokers: Map,
+ /// Mapping of token_hash to order_hash.
+ token_listings: Map,
+ /// Mapping of token_hash to auction details (order_hash and end_date, auction_offer_count).
+ auctions: Map,
+ /// Mapping of auction offer order_hash to auction listing order_hash.
+ auction_offers: Map,
+ }
+
+ // *************************************************************************
+ // EVENTS
+ // *************************************************************************
+
+ /// Events emitted by the Orderbook contract.
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ enum Event {
+ OrderPlaced: OrderPlaced,
+ OrderExecuted: OrderExecuted,
+ OrderCancelled: OrderCancelled,
+ RollbackStatus: RollbackStatus,
+ OrderFulfilled: OrderFulfilled,
+ }
+
+ // must be increased when `OrderPlaced` content change
+ const ORDER_PLACED_EVENT_VERSION: u8 = 1;
+ /// Event for when an order is placed.
+ #[derive(Drop, starknet::Event)]
+ struct OrderPlaced {
+ #[key]
+ order_hash: felt252,
+ #[key]
+ order_version: felt252,
+ #[key]
+ order_type: OrderType,
+ ///
+ version: u8,
+ // The order that was cancelled by this order.
+ cancelled_order_hash: Option,
+ // The full order serialized.
+ order: OrderV1,
+ }
+
+ // must be increased when `OrderExecuted` content change
+ const ORDER_EXECUTED_EVENT_VERSION: u8 = 2;
+ /// Event for when an order is executed.
+ #[derive(Drop, starknet::Event)]
+ struct OrderExecuted {
+ #[key]
+ order_hash: felt252,
+ #[key]
+ order_status: OrderStatus,
+ #[key]
+ order_type: OrderType,
+ ///
+ version: u8,
+ transaction_hash: felt252,
+ from: ContractAddress,
+ to: ContractAddress,
+ }
+
+ // must be increased when `OrderPlaced` content change
+ const ORDER_CANCELLED_EVENT_VERSION: u8 = 1;
+ /// Event for when an order is cancelled.
+ #[derive(Drop, starknet::Event)]
+ struct OrderCancelled {
+ #[key]
+ order_hash: felt252,
+ #[key]
+ reason: felt252,
+ #[key]
+ order_type: OrderType,
+ version: u8,
+ }
+
+ // must be increased when `RollbackStatus` content change
+ const ROLLBACK_STATUS_EVENT_VERSION: u8 = 1;
+ /// Event for when an order has been rollbacked to placed.
+ #[derive(Drop, starknet::Event)]
+ struct RollbackStatus {
+ #[key]
+ order_hash: felt252,
+ #[key]
+ reason: felt252,
+ #[key]
+ order_type: OrderType,
+ ///
+ version: u8,
+ }
+
+ // must be increased when `OrderFulfilled` content change
+ const ORDER_FULFILLED_EVENT_VERSION: u8 = 1;
+ /// Event for when an order is fulfilled.
+ #[derive(Drop, starknet::Event)]
+ struct OrderFulfilled {
+ #[key]
+ order_hash: felt252,
+ #[key]
+ fulfiller: ContractAddress,
+ #[key]
+ related_order_hash: Option,
+ #[key]
+ order_type: OrderType,
+ ///
+ version: u8,
+ }
+
+ pub trait OrderbookHooksCreateOrderTrait {
+ fn before_create_order(ref self: ComponentState, order: OrderV1) {}
+ fn after_create_order(ref self: ComponentState, order: OrderV1) {}
+ }
+
+ pub trait OrderbookHooksCancelOrderTrait {
+ fn before_cancel_order(ref self: ComponentState, cancel_info: CancelInfo) {}
+ fn after_cancel_order(ref self: ComponentState, cancel_info: CancelInfo) {}
+ }
+
+ pub trait OrderbookHooksFulfillOrderTrait {
+ fn before_fulfill_order(
+ ref self: ComponentState, fulfill_info: FulfillInfo
+ ) {}
+ fn after_fulfill_order(
+ ref self: ComponentState, fulfill_info: FulfillInfo
+ ) {}
+ }
+
+ pub trait OrderbookHooksValidateOrderExecutionTrait {
+ fn before_validate_order_execution(
+ ref self: ComponentState, info: ExecutionValidationInfo
+ ) {}
+ fn after_validate_order_execution(
+ ref self: ComponentState, info: ExecutionValidationInfo
+ ) {}
+ }
+
+ #[embeddable_as(OrderbookImpl)]
+ pub impl Orderbook<
+ TContractState, +HasComponent, +Drop,
+ > of IOrderbook> {
+ /// Retrieves the type of an order using its hash.
+ /// # View
+ fn get_order_type(self: @ComponentState, order_hash: felt252) -> OrderType {
+ let order_type_option = order_type_read(order_hash);
+ if order_type_option.is_none() {
+ panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
+ }
+ order_type_option.unwrap().into()
+ }
+
+ /// Retrieves the status of an order using its hash.
+ /// # View
+ fn get_order_status(
+ self: @ComponentState, order_hash: felt252
+ ) -> OrderStatus {
+ let status = order_status_read(order_hash);
+ if status.is_none() {
+ panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
+ }
+ status.unwrap().into()
+ }
+
+ /// Retrieves the auction end date
+ /// # View
+ fn get_auction_expiration(
+ self: @ComponentState, order_hash: felt252
+ ) -> u64 {
+ let order = order_read::(order_hash);
+ if (order.is_none()) {
+ panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
+ }
+ let token_hash = order.unwrap().compute_token_hash();
+ let (_, auction_end_date, _b) = self.auctions.read(token_hash);
+ auction_end_date
+ }
+
+ /// Retrieves the order using its hash.
+ /// # View
+ fn get_order(self: @ComponentState, order_hash: felt252) -> OrderV1 {
+ let order = order_read(order_hash);
+ if (order.is_none()) {
+ panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
+ }
+ order.unwrap()
+ }
+
+ /// Retrieves the order hash using its token hash.
+ /// # View
+ fn get_order_hash(self: @ComponentState, token_hash: felt252) -> felt252 {
+ let order_hash = self.token_listings.read(token_hash);
+ if (order_hash.is_zero()) {
+ panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
+ }
+ order_hash
+ }
+ }
+
+ pub impl OrderbookActionImpl<
+ TContractState,
+ +HasComponent,
+ impl HooksCreateOrder: OrderbookHooksCreateOrderTrait,
+ impl HooksCancelOrder: OrderbookHooksCancelOrderTrait,
+ impl HooksFulfillOrder: OrderbookHooksFulfillOrderTrait,
+ impl HooksValidateOrder: OrderbookHooksValidateOrderExecutionTrait,
+ > of IOrderbookAction> {
+ fn validate_order_execution(
+ ref self: ComponentState, info: ExecutionValidationInfo
+ ) {
+ HooksValidateOrder::before_validate_order_execution(ref self, info);
+ order_status_write(info.order_hash, OrderStatus::Executed);
+ let order_status = order_status_read(info.order_hash).unwrap();
+ let order_type = order_type_read(info.order_hash).unwrap();
+ self
+ .emit(
+ OrderExecuted {
+ order_hash: info.order_hash,
+ order_status,
+ order_type,
+ transaction_hash: info.transaction_hash,
+ from: info.from,
+ to: info.to,
+ version: ORDER_EXECUTED_EVENT_VERSION,
+ }
+ );
+
+ HooksValidateOrder::after_validate_order_execution(ref self, info);
+ }
+
+ /// Submits and places an order to the orderbook if the order is valid.
+ fn create_order(ref self: ComponentState, order: OrderV1) {
+ HooksCreateOrder::before_create_order(ref self, order);
+ let block_ts = starknet::get_block_timestamp();
+ let validation = order.validate_common_data(block_ts);
+ if validation.is_err() {
+ panic_with_felt252(validation.unwrap_err().into());
+ }
+ let order_type = order
+ .validate_order_type()
+ .expect(orderbook_errors::ORDER_INVALID_DATA);
+ let order_hash = order.compute_order_hash();
+ match order_type {
+ OrderType::Listing => {
+ assert(
+ order_status_read(order_hash).is_none(),
+ orderbook_errors::ORDER_ALREADY_EXISTS
+ );
+ let _ = self._create_listing_order(order, order_type, order_hash);
+ },
+ OrderType::Auction => {
+ assert(
+ order_status_read(order_hash).is_none(),
+ orderbook_errors::ORDER_ALREADY_EXISTS
+ );
+ self._create_auction(order, order_type, order_hash);
+ },
+ OrderType::Offer => { self._create_offer(order, order_type, order_hash); },
+ OrderType::CollectionOffer => {
+ self._create_collection_offer(order, order_type, order_hash);
+ },
+ };
+
+ HooksCreateOrder::after_create_order(ref self, order);
+ }
+
+ fn cancel_order(ref self: ComponentState, cancel_info: CancelInfo) {
+ HooksCancelOrder::before_cancel_order(ref self, cancel_info);
+
+ let order_hash = cancel_info.order_hash;
+ let order_option = order_read::(order_hash);
+ assert(order_option.is_some(), orderbook_errors::ORDER_NOT_FOUND);
+ let order = order_option.unwrap();
+ assert(order.offerer == cancel_info.canceller, 'not the same offerrer');
+ match order_status_read(order_hash) {
+ Option::Some(s) => s,
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+ let block_ts = starknet::get_block_timestamp();
+ let order_type = match order_type_read(order_hash) {
+ Option::Some(order_type) => {
+ if order_type == OrderType::Auction {
+ let auction_token_hash = order.compute_token_hash();
+ let (_, auction_end_date, _) = self.auctions.read(auction_token_hash);
+ assert(
+ block_ts <= auction_end_date, orderbook_errors::ORDER_AUCTION_IS_EXPIRED
+ );
+ self.auctions.write(auction_token_hash, (0, 0, 0));
+ } else {
+ assert(block_ts < order.end_date, orderbook_errors::ORDER_IS_EXPIRED);
+ if order_type == OrderType::Listing {
+ self.token_listings.write(order.compute_token_hash(), 0);
+ }
+ }
+ order_type
+ },
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+
+ // Cancel order
+ order_status_write(order_hash, OrderStatus::CancelledUser);
+ self
+ .emit(
+ OrderCancelled {
+ order_hash,
+ reason: OrderStatus::CancelledUser.into(),
+ order_type,
+ version: ORDER_CANCELLED_EVENT_VERSION,
+ }
+ );
+
+ HooksCancelOrder::after_cancel_order(ref self, cancel_info);
+ }
+
+ fn fulfill_order(
+ ref self: ComponentState, fulfill_info: FulfillInfo
+ ) -> Option:: {
+ HooksFulfillOrder::before_fulfill_order(ref self, fulfill_info);
+
+ let order_hash = fulfill_info.order_hash;
+ let order: OrderV1 = match order_read(order_hash) {
+ Option::Some(o) => o,
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+ let status = match order_status_read(order_hash) {
+ Option::Some(s) => s,
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+ assert(status == OrderStatus::Open, orderbook_errors::ORDER_NOT_FULFILLABLE);
+ let order_type = match order_type_read(order_hash) {
+ Option::Some(s) => s,
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+ let (execution_info, related_order_hash) = match order_type {
+ OrderType::Listing => self._fulfill_listing_order(fulfill_info, order),
+ OrderType::Auction => self._fulfill_auction_order(fulfill_info, order),
+ OrderType::Offer => self._fulfill_offer(fulfill_info, order),
+ OrderType::CollectionOffer => self._fulfill_offer(fulfill_info, order),
+ };
+
+ self
+ .emit(
+ OrderFulfilled {
+ order_hash: fulfill_info.order_hash,
+ fulfiller: fulfill_info.fulfiller,
+ related_order_hash,
+ order_type,
+ version: ORDER_FULFILLED_EVENT_VERSION,
+ }
+ );
+
+ HooksFulfillOrder::after_fulfill_order(ref self, fulfill_info);
+ execution_info
+ }
+ }
+
+ // *************************************************************************
+ // INTERNAL FUNCTIONS
+ // *************************************************************************
+ #[generate_trait]
+ pub impl InternalImpl<
+ TContractState, +HasComponent
+ > of InternalTrait {
+ /// Fulfill auction order
+ ///
+ /// # Arguments
+ /// * `fulfill_info` - The execution info of the order.
+ /// * `order_type` - The type of the order.
+ ///
+ fn _fulfill_auction_order(
+ ref self: ComponentState, fulfill_info: FulfillInfo, order: OrderV1
+ ) -> (Option, Option) {
+ let block_timestamp = starknet::get_block_timestamp();
+ assert(
+ order.offerer == fulfill_info.fulfiller, orderbook_errors::ORDER_NOT_SAME_OFFERER
+ );
+ // get auction end date from storage
+ let (_, end_date, _) = self.auctions.read(order.compute_token_hash());
+ assert(
+ end_date + AUCTION_ACCEPTING_TIME_SECS > block_timestamp,
+ orderbook_errors::ORDER_EXPIRED
+ );
+
+ let related_order_hash = fulfill_info
+ .related_order_hash
+ .expect(orderbook_errors::ORDER_MISSING_RELATED_ORDER);
+
+ match order_type_read(related_order_hash) {
+ Option::Some(order_type) => {
+ assert(
+ order_type == OrderType::Offer || order_type == OrderType::CollectionOffer,
+ orderbook_errors::ORDER_NOT_AN_OFFER
+ );
+ },
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ }
+
+ match order_status_read(related_order_hash) {
+ Option::Some(s) => {
+ assert(s == OrderStatus::Open, orderbook_errors::ORDER_NOT_OPEN);
+ s
+ },
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+
+ let related_order = match order_read::(related_order_hash) {
+ Option::Some(o) => o,
+ Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
+ };
+
+ let related_offer_auction = self.auction_offers.read(related_order_hash);
+
+ if related_offer_auction.is_non_zero() {
+ assert(
+ related_offer_auction == fulfill_info.order_hash,
+ orderbook_errors::ORDER_HASH_DOES_NOT_MATCH
+ );
+ } else {
+ assert(related_order.end_date > block_timestamp, orderbook_errors::ORDER_EXPIRED);
+ }
+ let related_order_token_hash = related_order.compute_token_hash();
+ assert(
+ related_order_token_hash == order.compute_token_hash(),
+ orderbook_errors::ORDER_TOKEN_HASH_DOES_NOT_MATCH
+ );
+ assert(
+ related_order.token_id == order.token_id,
+ orderbook_errors::ORDER_TOKEN_ID_DOES_NOT_MATCH
+ );
+
+ order_status_write(related_order_hash, OrderStatus::Fulfilled);
+ order_status_write(fulfill_info.order_hash, OrderStatus::Fulfilled);
+
+ if order.token_id.is_some() {
+ let execute_info = ExecutionInfo {
+ order_hash: order.compute_order_hash(),
+ nft_address: order.token_address,
+ nft_from: order.offerer,
+ nft_to: related_order.offerer,
+ nft_token_id: order.token_id.unwrap(),
+ payment_from: related_order.offerer,
+ payment_to: fulfill_info.fulfiller,
+ payment_amount: related_order.start_amount,
+ payment_currency_address: related_order.currency_address,
+ payment_currency_chain_id: related_order.currency_chain_id,
+ listing_broker_address: order.broker_id,
+ fulfill_broker_address: fulfill_info.fulfill_broker_address
+ };
+ (Option::Some(execute_info), Option::Some(related_order_hash))
+ } else {
+ (Option::None, Option::Some(related_order_hash))
+ }
+ }
+
+ /// Fulfill offer order
+ ///
+ /// # Arguments
+ /// * `fulfill_info` - The execution info of the order.
+ /// * `order` - The order.
+ ///
+ fn _fulfill_offer(
+ ref self: ComponentState, fulfill_info: FulfillInfo, order: OrderV1
+ ) -> (Option, Option) {
+ if order.token_id.is_some() {
+ let (auction_order_hash, _, _) = self.auctions.read(order.compute_token_hash());
+
+ assert(auction_order_hash.is_zero(), orderbook_errors::USE_FULFILL_AUCTION);
+ }
+
+ assert(fulfill_info.token_id.is_some(), orderbook_errors::ORDER_TOKEN_ID_IS_MISSING);
+
+ let current_date = starknet::get_block_timestamp();
+ assert(order.end_date > current_date, orderbook_errors::ORDER_EXPIRED);
+ order_status_write(fulfill_info.order_hash, OrderStatus::Fulfilled);
+
+ if order.token_id.is_some() {
+ // remove token from listed tokens
+ self.token_listings.write(order.compute_token_hash(), 0);
+ }
+
+ let execute_info = ExecutionInfo {
+ order_hash: order.compute_order_hash(),
+ nft_address: order.token_address,
+ nft_from: fulfill_info.fulfiller,
+ nft_to: order.offerer,
+ nft_token_id: fulfill_info.token_id.unwrap(),
+ payment_from: order.offerer,
+ payment_to: fulfill_info.fulfiller,
+ payment_amount: order.start_amount,
+ payment_currency_address: order.currency_address,
+ payment_currency_chain_id: order.currency_chain_id,
+ listing_broker_address: order.broker_id,
+ fulfill_broker_address: fulfill_info.fulfill_broker_address
+ };
+ (Option::Some(execute_info), Option::None)
+ }
+
+ /// Fulfill listing order
+ ///
+ /// # Arguments
+ /// * `fulfill_info` - The execution info of the order.
+ /// * `order_type` - The type of the order.
+ ///
+ fn _fulfill_listing_order(
+ ref self: ComponentState, fulfill_info: FulfillInfo, order: OrderV1
+ ) -> (Option, Option) {
+ assert(order.offerer != fulfill_info.fulfiller, orderbook_errors::ORDER_SAME_OFFERER);
+ assert(
+ order.end_date > starknet::get_block_timestamp(), orderbook_errors::ORDER_EXPIRED
+ );
+ order_status_write(fulfill_info.order_hash, OrderStatus::Fulfilled);
+
+ if order.token_id.is_some() {
+ let execute_info = ExecutionInfo {
+ order_hash: order.compute_order_hash(),
+ nft_address: order.token_address,
+ nft_from: order.offerer,
+ nft_to: fulfill_info.fulfiller,
+ nft_token_id: order.token_id.unwrap(),
+ payment_from: fulfill_info.fulfiller,
+ payment_to: order.offerer,
+ payment_amount: order.start_amount,
+ payment_currency_address: order.currency_address,
+ payment_currency_chain_id: order.currency_chain_id,
+ listing_broker_address: order.broker_id,
+ fulfill_broker_address: fulfill_info.fulfill_broker_address
+ };
+ (Option::Some(execute_info), Option::None)
+ } else {
+ (Option::None, Option::None)
+ }
+ }
+
+ /// Get order hash from token hash
+ ///
+ /// # Arguments
+ /// * `token_hash` - The token hash of the order.
+ ///
+ fn _get_order_hash_from_token_hash(
+ self: @ComponentState, token_hash: felt252
+ ) -> felt252 {
+ self.token_listings.read(token_hash)
+ }
+
+ /// get previous order
+ ///
+ /// # Arguments
+ /// * `token_hash` - The token hash of the order.
+ ///
+ /// # Return option of (order hash: felt252, is_order_expired: bool, order: OrderV1)
+ /// * order_hash
+ /// * is_order_expired
+ /// * order
+ fn _get_previous_order(
+ self: @ComponentState, token_hash: felt252
+ ) -> Option<(felt252, bool, OrderV1)> {
+ let previous_listing_orderhash = self.token_listings.read(token_hash);
+ let (previous_auction_orderhash, _, _) = self.auctions.read(token_hash);
+ let mut previous_orderhash = 0;
+ if (previous_listing_orderhash.is_non_zero()) {
+ previous_orderhash = previous_listing_orderhash;
+ let previous_order: Option = order_read(previous_orderhash);
+ assert(previous_order.is_some(), 'Order must exist');
+ let previous_order = previous_order.unwrap();
+ return Option::Some(
+ (
+ previous_orderhash,
+ previous_order.end_date <= starknet::get_block_timestamp(),
+ previous_order
+ )
+ );
+ }
+ if (previous_auction_orderhash.is_non_zero()) {
+ previous_orderhash = previous_auction_orderhash;
+ let current_order: Option = order_read(previous_orderhash);
+ assert(current_order.is_some(), 'Order must exist');
+ let current_order = current_order.unwrap();
+ let (_, auction_end_date, _) = self.auctions.read(token_hash);
+ return Option::Some(
+ (
+ previous_orderhash,
+ auction_end_date <= starknet::get_block_timestamp(),
+ current_order
+ )
+ );
+ } else {
+ return Option::None;
+ }
+ }
+
+ /// Process previous order
+ ///
+ /// # Arguments
+ /// * `token_hash` - The token hash of the order.
+ ///
+ fn _process_previous_order(
+ ref self: ComponentState, token_hash: felt252, offerer: ContractAddress
+ ) -> Option {
+ let previous_order = self._get_previous_order(token_hash);
+ if (previous_order.is_some()) {
+ let (previous_orderhash, previous_order_is_expired, previous_order) = previous_order
+ .unwrap();
+ let previous_order_status = order_status_read(previous_orderhash)
+ .expect('Invalid Order status');
+ assert(
+ previous_order_status != OrderStatus::Fulfilled,
+ orderbook_errors::ORDER_FULFILLED
+ );
+ if (previous_order.offerer == offerer) {
+ assert(previous_order_is_expired, orderbook_errors::ORDER_NOT_CANCELLABLE);
+ }
+ order_status_write(previous_orderhash, OrderStatus::CancelledByNewOrder);
+ return Option::Some(previous_orderhash);
+ }
+ return Option::None;
+ }
+
+ /// Creates a listing order.
+ fn _create_listing_order(
+ ref self: ComponentState,
+ order: OrderV1,
+ order_type: OrderType,
+ order_hash: felt252,
+ ) -> Option {
+ let token_hash = order.compute_token_hash();
+ // revert if order is fulfilled or Open
+ let current_order_hash = self.token_listings.read(token_hash);
+ if (current_order_hash.is_non_zero()) {
+ assert(
+ order_status_read(current_order_hash) != Option::Some(OrderStatus::Fulfilled),
+ orderbook_errors::ORDER_FULFILLED
+ );
+ }
+ let current_order: Option = order_read(current_order_hash);
+ if (current_order.is_some()) {
+ let current_order = current_order.unwrap();
+ // check if same offerer
+ if (current_order.offerer == order.offerer) {
+ // check expiration if order is expired continue
+ assert(
+ current_order.end_date <= starknet::get_block_timestamp(),
+ orderbook_errors::ORDER_ALREADY_EXISTS
+ );
+ }
+ }
+
+ let cancelled_order_hash = self._process_previous_order(token_hash, order.offerer);
+ order_write(order_hash, order_type, order);
+ self.token_listings.write(token_hash, order_hash);
+ self
+ .emit(
+ OrderPlaced {
+ order_hash: order_hash,
+ order_version: order.get_version(),
+ order_type: order_type,
+ version: ORDER_PLACED_EVENT_VERSION,
+ cancelled_order_hash,
+ order: order
+ }
+ );
+ cancelled_order_hash
+ }
+
+ /// Creates an auction order.
+ fn _create_auction(
+ ref self: ComponentState,
+ order: OrderV1,
+ order_type: OrderType,
+ order_hash: felt252
+ ) {
+ let token_hash = order.compute_token_hash();
+ let current_order_hash = self.token_listings.read(token_hash);
+ if (current_order_hash.is_non_zero()) {
+ assert(
+ order_status_read(current_order_hash) != Option::Some(OrderStatus::Fulfilled),
+ orderbook_errors::ORDER_FULFILLED
+ );
+ }
+ let current_order: Option = order_read(current_order_hash);
+ if (current_order.is_some()) {
+ let current_order = current_order.unwrap();
+ // check expiration if order is expired continue
+ if (current_order.offerer == order.offerer) {
+ assert(
+ current_order.end_date <= starknet::get_block_timestamp(),
+ orderbook_errors::ORDER_ALREADY_EXISTS
+ );
+ }
+ }
+ let token_hash = order.compute_token_hash();
+ let cancelled_order_hash = self._process_previous_order(token_hash, order.offerer);
+ order_write(order_hash, order_type, order);
+ self.auctions.write(token_hash, (order_hash, order.end_date, 0));
+ self
+ .emit(
+ OrderPlaced {
+ order_hash: order_hash,
+ order_version: order.get_version(),
+ order_type: order_type,
+ version: ORDER_PLACED_EVENT_VERSION,
+ cancelled_order_hash,
+ order: order,
+ }
+ );
+ }
+
+ fn _manage_auction_offer(
+ ref self: ComponentState, order: OrderV1, order_hash: felt252
+ ) {
+ let token_hash = order.compute_token_hash();
+ let (auction_order_hash, auction_end_date, auction_offer_count) = self
+ .auctions
+ .read(token_hash);
+
+ let current_block_timestamp = starknet::get_block_timestamp();
+ // Determine if the auction end date has passed, indicating that the auction is still
+ // ongoing.
+ let auction_is_pending = current_block_timestamp < auction_end_date;
+
+ if auction_is_pending {
+ // If the auction is still pending, record the new offer by linking it to the
+ // auction order hash in the 'auction_offers' mapping.
+ self.auction_offers.write(order_hash, auction_order_hash);
+
+ if auction_end_date - current_block_timestamp < EXTENSION_TIME_IN_SECONDS {
+ // Increment the number of offers for this auction and extend the auction
+ // end date by the predefined extension time to allow for additional offers.
+ self
+ .auctions
+ .write(
+ token_hash,
+ (
+ auction_order_hash,
+ auction_end_date + EXTENSION_TIME_IN_SECONDS,
+ auction_offer_count + 1
+ )
+ );
+ } else {
+ self
+ .auctions
+ .write(
+ token_hash,
+ (auction_order_hash, auction_end_date, auction_offer_count + 1)
+ );
+ }
+ }
+ }
+
+ /// Creates an offer order.
+ fn _create_offer(
+ ref self: ComponentState,
+ order: OrderV1,
+ order_type: OrderType,
+ order_hash: felt252
+ ) {
+ self._manage_auction_offer(order, order_hash);
+ order_write(order_hash, order_type, order);
+ self
+ .emit(
+ OrderPlaced {
+ order_hash: order_hash,
+ order_version: order.get_version(),
+ order_type: order_type,
+ version: ORDER_PLACED_EVENT_VERSION,
+ cancelled_order_hash: Option::None,
+ order: order,
+ }
+ );
+ }
+
+ /// Creates a collection offer order.
+ fn _create_collection_offer(
+ ref self: ComponentState,
+ order: OrderV1,
+ order_type: OrderType,
+ order_hash: felt252
+ ) {
+ order_write(order_hash, order_type, order);
+ self
+ .emit(
+ OrderPlaced {
+ order_hash: order_hash,
+ order_version: order.get_version(),
+ order_type,
+ version: ORDER_PLACED_EVENT_VERSION,
+ cancelled_order_hash: Option::None,
+ order: order,
+ }
+ );
+ }
+ }
+}
+pub impl OrderbookHooksCreateOrderEmptyImpl<
+ TContractState
+> of OrderbookComponent::OrderbookHooksCreateOrderTrait {}
+pub impl OrderbookHooksCancelOrderEmptyImpl<
+ TContractState
+> of OrderbookComponent::OrderbookHooksCancelOrderTrait {}
+pub impl OrderbookHooksFulfillOrderEmptyImpl<
+ TContractState
+> of OrderbookComponent::OrderbookHooksFulfillOrderTrait {}
+pub impl OrderbookHooksValidateOrderExecutionEmptyImpl<
+ TContractState
+> of OrderbookComponent::OrderbookHooksValidateOrderExecutionTrait {}
diff --git a/contracts/ark_orderbook/Scarb.toml b/contracts/ark_orderbook/Scarb.toml
index bfa59fd0d..3e2680078 100644
--- a/contracts/ark_orderbook/Scarb.toml
+++ b/contracts/ark_orderbook/Scarb.toml
@@ -4,12 +4,19 @@ version = "0.1.0"
[dependencies]
ark_common = { path = "../ark_common" }
-starknet = "2.5.4"
-snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.18.0" }
+ark_component = { path = "../ark_component" }
+starknet.workspace = true
+
+[dev-dependencies]
+snforge_std.workspace = true
+assert_macros.workspace = true
[scripts]
test.workspace = true
+[tool]
+fmt.workspace = true
+
[[target.starknet-contract]]
sierra = true
casm = true
diff --git a/contracts/ark_orderbook/src/orderbook.cairo b/contracts/ark_orderbook/src/orderbook.cairo
index 3db6318de..de68329e5 100644
--- a/contracts/ark_orderbook/src/orderbook.cairo
+++ b/contracts/ark_orderbook/src/orderbook.cairo
@@ -1,48 +1,17 @@
+use ark_common::crypto::signer::{SignInfo, Signer, SignerValidator};
//! # Orderbook Contract
//!
//! This module defines the structure and functionalities of an orderbook contract. It includes
-//! trait definitions, error handling, contract storage, events, constructors, L1 handlers, external functions,
-//! and internal functions. The primary functionalities include broker whitelisting, order management
-//! (creation, cancellation, fulfillment), and order queries.
+//! trait definitions, error handling, contract storage, events, constructors, L1 handlers, external
+//! functions, and internal functions. The primary functionalities include broker whitelisting,
+//! order management (creation, cancellation, fulfillment), and order queries.
use ark_common::protocol::order_types::{FulfillInfo, OrderType, CancelInfo, OrderStatus};
-use ark_common::crypto::signer::{SignInfo, Signer, SignerValidator};
use ark_common::protocol::order_v1::OrderV1;
use starknet::ContractAddress;
-/// Orderbook trait to define operations on orderbooks.
#[starknet::interface]
-trait Orderbook {
- /// Retrieves the type of an order using its hash.
- ///
- /// # Arguments
- /// * `order_hash` - The order hash of order.
- fn get_order_type(self: @T, order_hash: felt252) -> OrderType;
-
- /// Retrieves the status of an order using its hash.
- ///
- /// # Arguments
- /// * `order_hash` - The order hash of order.
- fn get_order_status(self: @T, order_hash: felt252) -> OrderStatus;
-
- /// Retrieves the auction end date.
- ///
- /// # Arguments
- /// * `order_hash` - The order hash of order.
- fn get_auction_expiration(self: @T, order_hash: felt252) -> u64;
-
- /// Retrieves the order using its hash.
- ///
- /// # Arguments
- /// * `order_hash` - The order hash of order.
- fn get_order(self: @T, order_hash: felt252) -> OrderV1;
-
- /// Retrieves the order hash using its token hash.
- ///
- /// # Arguments
- /// * `token_hash` - The token hash of the order.
- fn get_order_hash(self: @T, token_hash: felt252) -> felt252;
-
+trait OrderbookAdmin {
/// Upgrades the contract to a new version.
///
/// # Arguments
@@ -52,65 +21,41 @@ trait Orderbook {
fn update_starknet_executor_address(ref self: T, value: starknet::ContractAddress);
}
-// *************************************************************************
-// ERRORS
-//
-// Error messages used within the orderbook contract.
-// *************************************************************************
-mod orderbook_errors {
- const BROKER_UNREGISTERED: felt252 = 'OB: unregistered broker';
- const ORDER_INVALID_DATA: felt252 = 'OB: order invalid data';
- const ORDER_ALREADY_EXISTS: felt252 = 'OB: order already exists';
- const ORDER_ALREADY_EXEC: felt252 = 'OB: order already executed';
- const ORDER_NOT_FULFILLABLE: felt252 = 'OB: order not fulfillable';
- const ORDER_NOT_FOUND: felt252 = 'OB: order not found';
- const ORDER_FULFILLED: felt252 = 'OB: order fulfilled';
- const ORDER_NOT_CANCELLABLE: felt252 = 'OB: order not cancellable';
- const ORDER_EXPIRED: felt252 = 'OB: order expired';
- const ORDER_SAME_OFFERER: felt252 = 'OB: order has same offerer';
- const ORDER_NOT_SAME_OFFERER: felt252 = 'OB: fulfiller is not offerer';
- const OFFER_ALREADY_EXISTS: felt252 = 'OB: offer already exists';
- const ORDER_IS_EXPIRED: felt252 = 'OB: order is expired';
- const ORDER_AUCTION_IS_EXPIRED: felt252 = 'OB: auction is expired';
- const ORDER_MISSING_RELATED_ORDER: felt252 = 'OB: order missing related order';
- const ORDER_HASH_DOES_NOT_MATCH: felt252 = 'OB: order hash does not match';
- const ORDER_TOKEN_ID_DOES_NOT_MATCH: felt252 = 'OB: token id does not match';
- const ORDER_TOKEN_ID_IS_MISSING: felt252 = 'OB: token id is missing';
- const ORDER_TOKEN_HASH_DOES_NOT_MATCH: felt252 = 'OB: token hash does not match';
- const ORDER_NOT_AN_OFFER: felt252 = 'OB: order is not an offer';
- const ORDER_NOT_OPEN: felt252 = 'OB: order is not open';
- const ORDER_OPEN: felt252 = 'OB: order is not open';
- const USE_FULFILL_AUCTION: felt252 = 'OB: must use fulfill auction';
- const OFFER_NOT_STARTED: felt252 = 'OB: offer is not started';
- const INVALID_BROKER: felt252 = 'OB: broker is not whitelisted';
-}
/// StarkNet smart contract module for an order book.
#[starknet::contract]
mod orderbook {
- use ark_common::crypto::typed_data::{OrderSign, TypedDataTrait};
- use core::debug::PrintTrait;
+ use ark_common::crypto::hash::{serialized_hash};
use ark_common::crypto::signer::{SignInfo, Signer, SignerTrait, SignerValidator};
+ use ark_common::crypto::typed_data::{OrderSign, TypedDataTrait};
+ use ark_common::protocol::order_database::{
+ order_read, order_status_read, order_write, order_status_write, order_type_read
+ };
use ark_common::protocol::order_types::{
OrderStatus, OrderTrait, OrderType, CancelInfo, FulfillInfo, ExecutionValidationInfo,
ExecutionInfo, RouteType
};
- use ark_common::crypto::hash::{serialized_hash};
- use core::traits::TryInto;
- use core::result::ResultTrait;
- use core::zeroable::Zeroable;
- use core::option::OptionTrait;
- use core::starknet::event::EventEmitter;
- use core::traits::Into;
- use super::{orderbook_errors, Orderbook};
- use starknet::ContractAddress;
use ark_common::protocol::order_v1::OrderV1;
- use ark_common::protocol::order_database::{
- order_read, order_status_read, order_write, order_status_write, order_type_read
+ use ark_component::orderbook::OrderbookComponent;
+ use ark_component::orderbook::{
+ OrderbookHooksCreateOrderEmptyImpl, OrderbookHooksCancelOrderEmptyImpl,
+ OrderbookHooksFulfillOrderEmptyImpl, OrderbookHooksValidateOrderExecutionEmptyImpl,
};
+ use core::debug::PrintTrait;
+ use core::option::OptionTrait;
+ use core::result::ResultTrait;
+ use core::traits::Into;
+ use core::traits::TryInto;
+ use core::zeroable::Zeroable;
+ use starknet::ContractAddress;
+ use starknet::storage::Map;
+ use super::OrderbookAdmin;
const EXTENSION_TIME_IN_SECONDS: u64 = 600;
const AUCTION_ACCEPTING_TIME_SECS: u64 = 172800;
+
+ component!(path: OrderbookComponent, storage: orderbook, event: OrderbookEvent);
+
/// Storage struct for the Orderbook contract.
#[storage]
struct Storage {
@@ -121,17 +66,10 @@ mod orderbook {
chain_id: felt252,
/// Administrator address of the order book.
admin: ContractAddress,
- /// Mapping of broker addresses to their whitelisted status.
- /// Represented as felt252, set to 1 if the broker is registered.
- brokers: LegacyMap,
- /// Mapping of token_hash to order_hash.
- token_listings: LegacyMap,
- /// Mapping of token_hash to auction details (order_hash and end_date, auction_offer_count).
- auctions: LegacyMap,
- /// Mapping of auction offer order_hash to auction listing order_hash.
- auction_offers: LegacyMap,
/// The address of the StarkNet executor contract.
starknet_executor_address: ContractAddress,
+ #[substorage(v0)]
+ orderbook: OrderbookComponent::Storage,
}
// *************************************************************************
@@ -142,78 +80,21 @@ mod orderbook {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
- OrderPlaced: OrderPlaced,
- OrderExecuted: OrderExecuted,
- OrderCancelled: OrderCancelled,
- RollbackStatus: RollbackStatus,
- OrderFulfilled: OrderFulfilled,
+ #[flat]
+ OrderbookEvent: OrderbookComponent::Event,
Upgraded: Upgraded,
}
- /// Event for when an order is placed.
- #[derive(Drop, starknet::Event)]
- struct OrderPlaced {
- #[key]
- order_hash: felt252,
- #[key]
- order_version: felt252,
- #[key]
- order_type: OrderType,
- // The order that was cancelled by this order.
- cancelled_order_hash: Option,
- // The full order serialized.
- order: OrderV1,
- }
-
- // must be increased when `OrderExecuted` content change
- const ORDER_EXECUTED_EVENT_VERSION: u8 = 1;
- /// Event for when an order is executed.
- #[derive(Drop, starknet::Event)]
- struct OrderExecuted {
- #[key]
- order_hash: felt252,
- #[key]
- order_status: OrderStatus,
- version: u8,
- transaction_hash: felt252,
- from: ContractAddress,
- to: ContractAddress,
- }
-
- /// Event for when an order is cancelled.
- #[derive(Drop, starknet::Event)]
- struct OrderCancelled {
- #[key]
- order_hash: felt252,
- #[key]
- reason: felt252,
- }
-
- /// Event for when an order has been rollbacked to placed.
- #[derive(Drop, starknet::Event)]
- struct RollbackStatus {
- #[key]
- order_hash: felt252,
- #[key]
- reason: felt252
- }
-
- /// Event for when an order is fulfilled.
- #[derive(Drop, starknet::Event)]
- struct OrderFulfilled {
- #[key]
- order_hash: felt252,
- #[key]
- fulfiller: ContractAddress,
- #[key]
- related_order_hash: Option,
- }
-
#[derive(Drop, starknet::Event)]
struct Upgraded {
class_hash: starknet::ClassHash,
}
+ #[abi(embed_v0)]
+ impl OrderbookImpl = OrderbookComponent::OrderbookImpl;
+ impl OrderbookActionImpl = OrderbookComponent::OrderbookActionImpl;
+ impl OrderbookInternalImpl = OrderbookComponent::InternalImpl;
+
// *************************************************************************
// CONSTRUCTOR
// *************************************************************************
@@ -238,54 +119,39 @@ mod orderbook {
) {
// Solis already checks that ALL the messages are coming from the executor contract.
// TODO: anyway, it can be useful to have an extra check here.
- order_status_write(info.order_hash, OrderStatus::Executed);
- let order_status = order_status_read(info.order_hash).unwrap();
- self
- .emit(
- OrderExecuted {
- order_hash: info.order_hash,
- order_status: order_status,
- transaction_hash: info.transaction_hash,
- from: info.from,
- to: info.to,
- version: ORDER_EXECUTED_EVENT_VERSION,
- }
- );
+ self.orderbook.validate_order_execution(info);
}
/// Update status : only from solis.
- #[l1_handler]
- fn rollback_status_order(
- ref self: ContractState, _from_address: felt252, order_hash: felt252, reason: felt252
- ) {
- order_status_write(order_hash, OrderStatus::Open);
- self.emit(RollbackStatus { order_hash, reason: reason.into() });
- }
+ // #[l1_handler]
+ // fn rollback_status_order(
+ // ref self: ContractState, _from_address: felt252, order_hash: felt252, reason: felt252
+ // ) {
+ // order_status_write(order_hash, OrderStatus::Open);
+ // self.emit(RollbackStatus { order_hash, reason: reason.into() });
+ // }
#[l1_handler]
fn create_order_from_l2(ref self: ContractState, _from_address: felt252, order: OrderV1) {
- self._create_order(order);
+ self.orderbook.create_order(order);
}
#[l1_handler]
fn cancel_order_from_l2(
ref self: ContractState, _from_address: felt252, cancelInfo: CancelInfo
) {
- self._cancel_order(cancelInfo);
+ self.orderbook.cancel_order(cancelInfo);
}
#[l1_handler]
fn fulfill_order_from_l2(
ref self: ContractState, _from_address: felt252, fulfillInfo: FulfillInfo
) {
- self._fulfill_order(fulfillInfo);
+ let _ = self.orderbook.fulfill_order(fulfillInfo);
}
- // *************************************************************************
- // EXTERNAL FUNCTIONS
- // *************************************************************************
#[abi(embed_v0)]
- impl ImplOrderbook of Orderbook {
+ impl OrderbookAdminImpl of OrderbookAdmin {
fn upgrade(ref self: ContractState, class_hash: starknet::ClassHash) {
assert(
starknet::get_caller_address() == self.admin.read(), 'Unauthorized replace class'
@@ -301,155 +167,24 @@ mod orderbook {
assert(starknet::get_caller_address() == self.admin.read(), 'Unauthorized update');
self.starknet_executor_address.write(value);
}
-
- /// Retrieves the type of an order using its hash.
- /// # View
- fn get_order_type(self: @ContractState, order_hash: felt252) -> OrderType {
- let order_type_option = order_type_read(order_hash);
- if order_type_option.is_none() {
- panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
- }
- order_type_option.unwrap().into()
- }
-
- /// Retrieves the status of an order using its hash.
- /// # View
- fn get_order_status(self: @ContractState, order_hash: felt252) -> OrderStatus {
- let status = order_status_read(order_hash);
- if status.is_none() {
- panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
- }
- status.unwrap().into()
- }
-
- /// Retrieves the auction end date
- /// # View
- fn get_auction_expiration(self: @ContractState, order_hash: felt252) -> u64 {
- let order = order_read::(order_hash);
- if (order.is_none()) {
- panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
- }
- let token_hash = order.unwrap().compute_token_hash();
- let (_, auction_end_date, _) = self.auctions.read(token_hash);
- auction_end_date
- }
-
- /// Retrieves the order using its hash.
- /// # View
- fn get_order(self: @ContractState, order_hash: felt252) -> OrderV1 {
- let order = order_read(order_hash);
- if (order.is_none()) {
- panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
- }
- order.unwrap()
- }
-
- /// Retrieves the order hash using its token hash.
- /// # View
- fn get_order_hash(self: @ContractState, token_hash: felt252) -> felt252 {
- let order_hash = self.token_listings.read(token_hash);
- if (order_hash.is_zero()) {
- panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND);
- }
- order_hash
- }
}
// *************************************************************************
- // INTERNAL FUNCTIONS
+ // INTERNAL FUNCTIONS FOR TESTING PURPOSE
// *************************************************************************
#[generate_trait]
impl InternalFunctions of InternalFunctionsTrait {
- // TODO: kwiss this code repeat itself with the public function, we should refactor it.
fn _cancel_order(ref self: ContractState, cancel_info: CancelInfo) {
- let order_hash = cancel_info.order_hash;
- let order_option = order_read::(order_hash);
- assert(order_option.is_some(), orderbook_errors::ORDER_NOT_FOUND);
- let order = order_option.unwrap();
- assert(order.offerer == cancel_info.canceller, 'not the same offerrer');
- match order_status_read(order_hash) {
- Option::Some(s) => s,
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
- let block_ts = starknet::get_block_timestamp();
- match order_type_read(order_hash) {
- Option::Some(order_type) => {
- if order_type == OrderType::Auction {
- let auction_token_hash = order.compute_token_hash();
- let (_, auction_end_date, _) = self.auctions.read(auction_token_hash);
- assert(
- block_ts <= auction_end_date, orderbook_errors::ORDER_AUCTION_IS_EXPIRED
- );
- self.auctions.write(auction_token_hash, (0, 0, 0));
- } else {
- assert(block_ts < order.end_date, orderbook_errors::ORDER_IS_EXPIRED);
- if order_type == OrderType::Listing {
- self.token_listings.write(order.compute_token_hash(), 0);
- }
- }
- },
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
-
- // Cancel order
- order_status_write(order_hash, OrderStatus::CancelledUser);
- self.emit(OrderCancelled { order_hash, reason: OrderStatus::CancelledUser.into() });
+ self.orderbook.cancel_order(cancel_info);
}
/// Submits and places an order to the orderbook if the order is valid.
fn _create_order(ref self: ContractState, order: OrderV1) {
- let block_ts = starknet::get_block_timestamp();
- let validation = order.validate_common_data(block_ts);
- if validation.is_err() {
- panic_with_felt252(validation.unwrap_err().into());
- }
- let order_type = order
- .validate_order_type()
- .expect(orderbook_errors::ORDER_INVALID_DATA);
- let order_hash = order.compute_order_hash();
- match order_type {
- OrderType::Listing => {
- assert(
- order_status_read(order_hash).is_none(),
- orderbook_errors::ORDER_ALREADY_EXISTS
- );
- let _ = self._create_listing_order(order, order_type, order_hash);
- },
- OrderType::Auction => {
- assert(
- order_status_read(order_hash).is_none(),
- orderbook_errors::ORDER_ALREADY_EXISTS
- );
- self._create_auction(order, order_type, order_hash);
- },
- OrderType::Offer => { self._create_offer(order, order_type, order_hash); },
- OrderType::CollectionOffer => {
- self._create_collection_offer(order, order_type, order_hash);
- },
- };
+ self.orderbook.create_order(order);
}
fn _fulfill_order(ref self: ContractState, fulfill_info: FulfillInfo) {
- let order_hash = fulfill_info.order_hash;
- let order: OrderV1 = match order_read(order_hash) {
- Option::Some(o) => o,
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
- let status = match order_status_read(order_hash) {
- Option::Some(s) => s,
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
- assert(status == OrderStatus::Open, orderbook_errors::ORDER_NOT_FULFILLABLE);
- let order_type = match order_type_read(order_hash) {
- Option::Some(s) => s,
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
- match order_type {
- OrderType::Listing => { self._fulfill_listing_order(fulfill_info, order); },
- OrderType::Auction => { self._fulfill_auction_order(fulfill_info, order) },
- OrderType::Offer => { self._fulfill_offer(fulfill_info, order); },
- OrderType::CollectionOffer => { self._fulfill_offer(fulfill_info, order); }
- }
+ let _ = self.orderbook.fulfill_order(fulfill_info);
}
/// Fulfill auction order
@@ -461,99 +196,21 @@ mod orderbook {
fn _fulfill_auction_order(
ref self: ContractState, fulfill_info: FulfillInfo, order: OrderV1
) {
- let block_timestamp = starknet::get_block_timestamp();
- assert(
- order.offerer == fulfill_info.fulfiller, orderbook_errors::ORDER_NOT_SAME_OFFERER
- );
- // get auction end date from storage
- let (_, end_date, _) = self.auctions.read(order.compute_token_hash());
- assert(
- end_date + AUCTION_ACCEPTING_TIME_SECS > block_timestamp,
- orderbook_errors::ORDER_EXPIRED
- );
-
- let related_order_hash = fulfill_info
- .related_order_hash
- .expect(orderbook_errors::ORDER_MISSING_RELATED_ORDER);
-
- match order_type_read(related_order_hash) {
- Option::Some(order_type) => {
- assert(
- order_type == OrderType::Offer || order_type == OrderType::CollectionOffer,
- orderbook_errors::ORDER_NOT_AN_OFFER
- );
- },
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- }
-
- match order_status_read(related_order_hash) {
- Option::Some(s) => {
- assert(s == OrderStatus::Open, orderbook_errors::ORDER_NOT_OPEN);
- s
+ let (execute_info, _) = self.orderbook._fulfill_auction_order(fulfill_info, order);
+ match execute_info {
+ Option::Some(execute_info) => {
+ let execute_order_selector = selector!("execute_order");
+ let starknet_executor_address: ContractAddress = self
+ .starknet_executor_address
+ .read();
+
+ let mut buf: Array = array![
+ starknet_executor_address.into(), execute_order_selector
+ ];
+ execute_info.serialize(ref buf);
+ starknet::send_message_to_l1_syscall('EXE', buf.span()).unwrap();
},
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
-
- let related_order = match order_read::(related_order_hash) {
- Option::Some(o) => o,
- Option::None => panic_with_felt252(orderbook_errors::ORDER_NOT_FOUND),
- };
-
- let related_offer_auction = self.auction_offers.read(related_order_hash);
-
- if related_offer_auction.is_non_zero() {
- assert(
- related_offer_auction == fulfill_info.order_hash,
- orderbook_errors::ORDER_HASH_DOES_NOT_MATCH
- );
- } else {
- assert(related_order.end_date > block_timestamp, orderbook_errors::ORDER_EXPIRED);
- }
- let related_order_token_hash = related_order.compute_token_hash();
- assert(
- related_order_token_hash == order.compute_token_hash(),
- orderbook_errors::ORDER_TOKEN_HASH_DOES_NOT_MATCH
- );
- assert(
- related_order.token_id == order.token_id,
- orderbook_errors::ORDER_TOKEN_ID_DOES_NOT_MATCH
- );
-
- order_status_write(related_order_hash, OrderStatus::Fulfilled);
- order_status_write(fulfill_info.order_hash, OrderStatus::Fulfilled);
- self
- .emit(
- OrderFulfilled {
- order_hash: fulfill_info.order_hash,
- fulfiller: fulfill_info.fulfiller,
- related_order_hash: Option::Some(related_order_hash)
- }
- );
-
- let execute_order_selector = selector!("execute_order");
- let starknet_executor_address: ContractAddress = self.starknet_executor_address.read();
-
- let mut buf: Array = array![
- starknet_executor_address.into(), execute_order_selector
- ];
- // execute order
- if order.token_id.is_some() {
- let execute_info = ExecutionInfo {
- order_hash: order.compute_order_hash(),
- nft_address: order.token_address,
- nft_from: order.offerer,
- nft_to: related_order.offerer,
- nft_token_id: order.token_id.unwrap(),
- payment_from: related_order.offerer,
- payment_to: fulfill_info.fulfiller,
- payment_amount: related_order.start_amount,
- payment_currency_address: related_order.currency_address,
- payment_currency_chain_id: related_order.currency_chain_id,
- listing_broker_address: order.broker_id,
- fulfill_broker_address: fulfill_info.fulfill_broker_address
- };
- execute_info.serialize(ref buf);
- starknet::send_message_to_l1_syscall('EXE', buf.span()).unwrap();
+ Option::None => ()
}
}
@@ -564,54 +221,22 @@ mod orderbook {
/// * `order` - The order.
///
fn _fulfill_offer(ref self: ContractState, fulfill_info: FulfillInfo, order: OrderV1) {
- if order.token_id.is_some() {
- let (auction_order_hash, _, _) = self.auctions.read(order.compute_token_hash());
-
- assert(auction_order_hash.is_zero(), orderbook_errors::USE_FULFILL_AUCTION);
- }
-
- assert(fulfill_info.token_id.is_some(), orderbook_errors::ORDER_TOKEN_ID_IS_MISSING);
-
- let current_date = starknet::get_block_timestamp();
- assert(order.end_date > current_date, orderbook_errors::ORDER_EXPIRED);
-
- order_status_write(fulfill_info.order_hash, OrderStatus::Fulfilled);
- self
- .emit(
- OrderFulfilled {
- order_hash: fulfill_info.order_hash,
- fulfiller: fulfill_info.fulfiller,
- related_order_hash: Option::None
- }
- );
-
- let execute_order_selector = selector!("execute_order");
- let starknet_executor_address: ContractAddress = self.starknet_executor_address.read();
-
- let mut buf: Array = array![
- starknet_executor_address.into(), execute_order_selector
- ];
-
- if order.token_id.is_some() {
- // remove token from listed tokens
- self.token_listings.write(order.compute_token_hash(), 0);
+ let (execute_info, _) = self.orderbook._fulfill_offer(fulfill_info, order);
+ match execute_info {
+ Option::Some(execute_info) => {
+ let execute_order_selector = selector!("execute_order");
+ let starknet_executor_address: ContractAddress = self
+ .starknet_executor_address
+ .read();
+
+ let mut buf: Array = array![
+ starknet_executor_address.into(), execute_order_selector
+ ];
+ execute_info.serialize(ref buf);
+ starknet::send_message_to_l1_syscall('EXE', buf.span()).unwrap();
+ },
+ Option::None => ()
}
- let execute_info = ExecutionInfo {
- order_hash: order.compute_order_hash(),
- nft_address: order.token_address,
- nft_from: fulfill_info.fulfiller,
- nft_to: order.offerer,
- nft_token_id: fulfill_info.token_id.unwrap(),
- payment_from: order.offerer,
- payment_to: fulfill_info.fulfiller,
- payment_amount: order.start_amount,
- payment_currency_address: order.currency_address,
- payment_currency_chain_id: order.currency_chain_id,
- listing_broker_address: order.broker_id,
- fulfill_broker_address: fulfill_info.fulfill_broker_address
- };
- execute_info.serialize(ref buf);
- starknet::send_message_to_l1_syscall('EXE', buf.span()).unwrap();
}
/// Fulfill listing order
@@ -623,44 +248,22 @@ mod orderbook {
fn _fulfill_listing_order(
ref self: ContractState, fulfill_info: FulfillInfo, order: OrderV1
) {
- assert(order.offerer != fulfill_info.fulfiller, orderbook_errors::ORDER_SAME_OFFERER);
- assert(
- order.end_date > starknet::get_block_timestamp(), orderbook_errors::ORDER_EXPIRED
- );
- order_status_write(fulfill_info.order_hash, OrderStatus::Fulfilled);
- self
- .emit(
- OrderFulfilled {
- order_hash: fulfill_info.order_hash,
- fulfiller: fulfill_info.fulfiller,
- related_order_hash: Option::None
- }
- );
-
- let execute_order_selector = selector!("execute_order");
- let starknet_executor_address: ContractAddress = self.starknet_executor_address.read();
-
- let mut buf: Array = array![
- starknet_executor_address.into(), execute_order_selector
- ];
-
- if order.token_id.is_some() {
- let execute_info = ExecutionInfo {
- order_hash: order.compute_order_hash(),
- nft_address: order.token_address,
- nft_from: order.offerer,
- nft_to: fulfill_info.fulfiller,
- nft_token_id: order.token_id.unwrap(),
- payment_from: fulfill_info.fulfiller,
- payment_to: order.offerer,
- payment_amount: order.start_amount,
- payment_currency_address: order.currency_address,
- payment_currency_chain_id: order.currency_chain_id,
- listing_broker_address: order.broker_id,
- fulfill_broker_address: fulfill_info.fulfill_broker_address
- };
- execute_info.serialize(ref buf);
- starknet::send_message_to_l1_syscall('EXE', buf.span()).unwrap();
+ let (execute_info, _) = self.orderbook._fulfill_listing_order(fulfill_info, order);
+ match execute_info {
+ Option::Some(execute_info) => {
+ let execute_order_selector = selector!("execute_order");
+ let starknet_executor_address: ContractAddress = self
+ .starknet_executor_address
+ .read();
+
+ let mut buf: Array = array![
+ starknet_executor_address.into(), execute_order_selector
+ ];
+
+ execute_info.serialize(ref buf);
+ starknet::send_message_to_l1_syscall('EXE', buf.span()).unwrap();
+ },
+ Option::None => {}
}
}
@@ -670,7 +273,7 @@ mod orderbook {
/// * `token_hash` - The token hash of the order.
///
fn _get_order_hash_from_token_hash(self: @ContractState, token_hash: felt252) -> felt252 {
- self.token_listings.read(token_hash)
+ self.orderbook._get_order_hash_from_token_hash(token_hash)
}
/// get previous order
@@ -685,38 +288,7 @@ mod orderbook {
fn _get_previous_order(
self: @ContractState, token_hash: felt252
) -> Option<(felt252, bool, OrderV1)> {
- let previous_listing_orderhash = self.token_listings.read(token_hash);
- let (previous_auction_orderhash, _, _) = self.auctions.read(token_hash);
- let mut previous_orderhash = 0;
- if (previous_listing_orderhash.is_non_zero()) {
- previous_orderhash = previous_listing_orderhash;
- let previous_order: Option = order_read(previous_orderhash);
- assert(previous_order.is_some(), 'Order must exist');
- let previous_order = previous_order.unwrap();
- return Option::Some(
- (
- previous_orderhash,
- previous_order.end_date <= starknet::get_block_timestamp(),
- previous_order
- )
- );
- }
- if (previous_auction_orderhash.is_non_zero()) {
- previous_orderhash = previous_auction_orderhash;
- let current_order: Option = order_read(previous_orderhash);
- assert(current_order.is_some(), 'Order must exist');
- let current_order = current_order.unwrap();
- let (_, auction_end_date, _) = self.auctions.read(token_hash);
- return Option::Some(
- (
- previous_orderhash,
- auction_end_date <= starknet::get_block_timestamp(),
- current_order
- )
- );
- } else {
- return Option::None;
- }
+ self.orderbook._get_previous_order(token_hash)
}
/// Process previous order
@@ -727,178 +299,39 @@ mod orderbook {
fn _process_previous_order(
ref self: ContractState, token_hash: felt252, offerer: ContractAddress
) -> Option {
- let previous_order = self._get_previous_order(token_hash);
- if (previous_order.is_some()) {
- let (previous_orderhash, previous_order_is_expired, previous_order) = previous_order
- .unwrap();
- let previous_order_status = order_status_read(previous_orderhash)
- .expect('Invalid Order status');
- assert(
- previous_order_status != OrderStatus::Fulfilled,
- orderbook_errors::ORDER_FULFILLED
- );
- if (previous_order.offerer == offerer) {
- assert(previous_order_is_expired, orderbook_errors::ORDER_NOT_CANCELLABLE);
- }
- order_status_write(previous_orderhash, OrderStatus::CancelledByNewOrder);
- return Option::Some(previous_orderhash);
- }
- return Option::None;
+ self.orderbook._process_previous_order(token_hash, offerer)
}
/// Creates a listing order.
fn _create_listing_order(
ref self: ContractState, order: OrderV1, order_type: OrderType, order_hash: felt252,
) -> Option {
- let token_hash = order.compute_token_hash();
- // revert if order is fulfilled or Open
- let current_order_hash = self.token_listings.read(token_hash);
- if (current_order_hash.is_non_zero()) {
- assert(
- order_status_read(current_order_hash) != Option::Some(OrderStatus::Fulfilled),
- orderbook_errors::ORDER_FULFILLED
- );
- }
- let current_order: Option = order_read(current_order_hash);
- if (current_order.is_some()) {
- let current_order = current_order.unwrap();
- // check if same offerer
- if (current_order.offerer == order.offerer) {
- // check expiration if order is expired continue
- assert(
- current_order.end_date <= starknet::get_block_timestamp(),
- orderbook_errors::ORDER_ALREADY_EXISTS
- );
- }
- }
-
- let cancelled_order_hash = self._process_previous_order(token_hash, order.offerer);
- order_write(order_hash, order_type, order);
- self.token_listings.write(token_hash, order_hash);
- self
- .emit(
- OrderPlaced {
- order_hash: order_hash,
- order_version: order.get_version(),
- order_type: order_type,
- cancelled_order_hash,
- order: order
- }
- );
- cancelled_order_hash
+ self.orderbook._create_listing_order(order, order_type, order_hash)
}
/// Creates an auction order.
fn _create_auction(
ref self: ContractState, order: OrderV1, order_type: OrderType, order_hash: felt252
) {
- let token_hash = order.compute_token_hash();
- let current_order_hash = self.token_listings.read(token_hash);
- if (current_order_hash.is_non_zero()) {
- assert(
- order_status_read(current_order_hash) != Option::Some(OrderStatus::Fulfilled),
- orderbook_errors::ORDER_FULFILLED
- );
- }
- let current_order: Option = order_read(current_order_hash);
- if (current_order.is_some()) {
- let current_order = current_order.unwrap();
- // check expiration if order is expired continue
- if (current_order.offerer == order.offerer) {
- assert(
- current_order.end_date <= starknet::get_block_timestamp(),
- orderbook_errors::ORDER_ALREADY_EXISTS
- );
- }
- }
- let token_hash = order.compute_token_hash();
- let cancelled_order_hash = self._process_previous_order(token_hash, order.offerer);
- order_write(order_hash, order_type, order);
- self.auctions.write(token_hash, (order_hash, order.end_date, 0));
- self
- .emit(
- OrderPlaced {
- order_hash: order_hash,
- order_version: order.get_version(),
- order_type: order_type,
- cancelled_order_hash,
- order: order,
- }
- );
+ self.orderbook._create_auction(order, order_type, order_hash)
}
fn _manage_auction_offer(ref self: ContractState, order: OrderV1, order_hash: felt252) {
- let token_hash = order.compute_token_hash();
- let (auction_order_hash, auction_end_date, auction_offer_count) = self
- .auctions
- .read(token_hash);
-
- let current_block_timestamp = starknet::get_block_timestamp();
- // Determine if the auction end date has passed, indicating that the auction is still ongoing.
- let auction_is_pending = current_block_timestamp < auction_end_date;
-
- if auction_is_pending {
- // If the auction is still pending, record the new offer by linking it to the
- // auction order hash in the 'auction_offers' mapping.
- self.auction_offers.write(order_hash, auction_order_hash);
-
- if auction_end_date - current_block_timestamp < EXTENSION_TIME_IN_SECONDS {
- // Increment the number of offers for this auction and extend the auction
- // end date by the predefined extension time to allow for additional offers.
- self
- .auctions
- .write(
- token_hash,
- (
- auction_order_hash,
- auction_end_date + EXTENSION_TIME_IN_SECONDS,
- auction_offer_count + 1
- )
- );
- } else {
- self
- .auctions
- .write(
- token_hash,
- (auction_order_hash, auction_end_date, auction_offer_count + 1)
- );
- }
- }
+ self.orderbook._manage_auction_offer(order, order_hash)
}
/// Creates an offer order.
fn _create_offer(
ref self: ContractState, order: OrderV1, order_type: OrderType, order_hash: felt252
) {
- self._manage_auction_offer(order, order_hash);
- order_write(order_hash, order_type, order);
- self
- .emit(
- OrderPlaced {
- order_hash: order_hash,
- order_version: order.get_version(),
- order_type: order_type,
- cancelled_order_hash: Option::None,
- order: order,
- }
- );
+ self.orderbook._create_offer(order, order_type, order_hash)
}
/// Creates a collection offer order.
fn _create_collection_offer(
ref self: ContractState, order: OrderV1, order_type: OrderType, order_hash: felt252
) {
- order_write(order_hash, order_type, order);
- self
- .emit(
- OrderPlaced {
- order_hash: order_hash,
- order_version: order.get_version(),
- order_type: order_type,
- cancelled_order_hash: Option::None,
- order: order,
- }
- );
+ self.orderbook._create_collection_offer(order, order_type, order_hash)
}
}
}
diff --git a/contracts/ark_orderbook/src/orderbook_event_mock.cairo b/contracts/ark_orderbook/src/orderbook_event_mock.cairo
index 1e94d05ba..a0ac3b179 100644
--- a/contracts/ark_orderbook/src/orderbook_event_mock.cairo
+++ b/contracts/ark_orderbook/src/orderbook_event_mock.cairo
@@ -8,14 +8,14 @@ mod orderbook_event_mock {
use ark_common::protocol::order_types::{
FulfillInfo, OrderType, CancelInfo, OrderStatus, RouteType
};
- use core::traits::TryInto;
- use core::result::ResultTrait;
- use core::zeroable::Zeroable;
+ use ark_common::protocol::order_v1::OrderV1;
use core::option::OptionTrait;
+ use core::result::ResultTrait;
use core::starknet::event::EventEmitter;
use core::traits::Into;
+ use core::traits::TryInto;
+ use core::zeroable::Zeroable;
use starknet::ContractAddress;
- use ark_common::protocol::order_v1::OrderV1;
#[storage]
struct Storage {}
diff --git a/contracts/ark_orderbook/tests/common/setup.cairo b/contracts/ark_orderbook/tests/common/setup.cairo
index 27349582a..17f3260db 100644
--- a/contracts/ark_orderbook/tests/common/setup.cairo
+++ b/contracts/ark_orderbook/tests/common/setup.cairo
@@ -1,17 +1,18 @@
-use core::traits::TryInto;
-use core::option::OptionTrait;
-use core::traits::Into;
-use ark_common::protocol::order_types::{RouteType, FulfillInfo, OrderTrait, OrderType, OrderStatus};
use ark_common::crypto::signer::{Signer, SignInfo};
+use ark_common::protocol::order_types::{RouteType, FulfillInfo, OrderTrait, OrderType, OrderStatus};
use ark_common::protocol::order_v1::OrderV1;
-use ark_orderbook::orderbook::{OrderbookDispatcher, OrderbookDispatcherTrait};
+use ark_component::orderbook::interface::IOrderbookDispatcher as OrderbookDispatcher;
+use ark_component::orderbook::interface::IOrderbookDispatcherTrait as OrderbookDispatcherTrait;
+use core::option::OptionTrait;
+use core::traits::Into;
+use core::traits::TryInto;
use snforge_std::signature::KeyPairTrait;
use snforge_std::signature::stark_curve::{
StarkCurveKeyPairImpl, StarkCurveSignerImpl, StarkCurveVerifierImpl
};
+use snforge_std::test_address;
-use snforge_std::{start_prank, stop_prank, test_address, CheatTarget};
use starknet::ContractAddress;
/// Utility function to setup orders for test environment.
diff --git a/contracts/ark_orderbook/tests/lib.cairo b/contracts/ark_orderbook/tests/lib.cairo
index 411f07586..d13e9d89b 100644
--- a/contracts/ark_orderbook/tests/lib.cairo
+++ b/contracts/ark_orderbook/tests/lib.cairo
@@ -3,8 +3,8 @@ mod common {
}
mod unit {
+ mod test_orderbook;
mod order {
mod test_order_v1;
}
- mod test_orderbook;
}
diff --git a/contracts/ark_orderbook/tests/unit/order/test_order_v1.cairo b/contracts/ark_orderbook/tests/unit/order/test_order_v1.cairo
index 479bb8221..c73c09d54 100644
--- a/contracts/ark_orderbook/tests/unit/order/test_order_v1.cairo
+++ b/contracts/ark_orderbook/tests/unit/order/test_order_v1.cairo
@@ -1,20 +1,6 @@
-use core::option::OptionTrait;
-use core::result::ResultTrait;
-use core::traits::Into;
-use core::traits::TryInto;
-use ark_common::protocol::order_v1::{OrderV1, OrderTraitOrderV1};
-use ark_orderbook::orderbook::{
- orderbook, orderbook_errors, OrderbookDispatcher, OrderbookDispatcherTrait
-};
-use ark_common::protocol::order_types::{OrderType, OrderTrait, RouteType};
-use ark_common::crypto::signer::{SignInfo, Signer, SignerValidator};
-use debug::PrintTrait;
+use ark_common::protocol::order_types::OrderType;
+use ark_common::protocol::order_v1::OrderTraitOrderV1;
use super::super::super::common::setup::setup_orders;
-use snforge_std::{ContractClassTrait, declare};
-
-// *********************************************************
-// validate_common_data
-// *********************************************************
#[test]
fn test_validate_common_data_with_valid_order() {
diff --git a/contracts/ark_orderbook/tests/unit/test_orderbook.cairo b/contracts/ark_orderbook/tests/unit/test_orderbook.cairo
index 19c5c4918..3aa12b467 100644
--- a/contracts/ark_orderbook/tests/unit/test_orderbook.cairo
+++ b/contracts/ark_orderbook/tests/unit/test_orderbook.cairo
@@ -1,19 +1,20 @@
-use core::option::OptionTrait;
-use ark_orderbook::orderbook::{orderbook, orderbook_errors};
-use ark_common::protocol::order_v1::OrderV1;
-use core::traits::Into;
-use core::traits::TryInto;
-use snforge_std::cheatcodes::CheatTarget;
use ark_common::crypto::signer::{SignInfo, Signer, SignerValidator};
-use ark_common::protocol::order_types::{OrderTrait, RouteType, OrderType, FulfillInfo, OrderStatus};
use ark_common::protocol::order_database::{
order_read, order_status_read, order_status_write, order_type_read
};
+use ark_common::protocol::order_types::{OrderTrait, RouteType, OrderType, FulfillInfo, OrderStatus};
+
+use ark_common::protocol::order_v1::OrderV1;
+use ark_component::orderbook::OrderbookComponent;
+use ark_orderbook::orderbook::orderbook;
+use array::ArrayTrait;
+use core::option::OptionTrait;
+use core::traits::Into;
+use core::traits::TryInto;
use snforge_std::{
- start_warp, declare, ContractClassTrait, spy_events, EventSpy, EventFetcher, EventAssertions,
- Event, SpyOn, test_address
+ ContractClassTrait, spy_events, EventSpyAssertionsTrait, EventSpyTrait, Event, test_address,
+ cheat_block_timestamp, CheatSpan,
};
-use array::ArrayTrait;
use super::super::common::setup::{setup_listing_order, get_offer_order, setup_orders};
@@ -24,7 +25,7 @@ fn test_create_listing() {
let (order_listing_1, order_hash_1, _) = setup_listing_order(600000000000000000);
let contract_address = test_address();
let mut state = orderbook::contract_state_for_testing();
- let mut spy = spy_events(SpyOn::One(contract_address));
+ let mut spy = spy_events();
let _ = orderbook::InternalFunctions::_create_listing_order(
ref state, order_listing_1, OrderType::Listing, order_hash_1
@@ -57,12 +58,13 @@ fn test_create_listing() {
@array![
(
contract_address,
- orderbook::Event::OrderPlaced(
- orderbook::OrderPlaced {
+ OrderbookComponent::Event::OrderPlaced(
+ OrderbookComponent::OrderPlaced {
order_hash: order_hash_1,
cancelled_order_hash: Option::None,
order_version: ORDER_VERSION_V1,
order_type: OrderType::Listing,
+ version: OrderbookComponent::ORDER_PLACED_EVENT_VERSION,
order: order_listing_1
}
)
@@ -102,8 +104,10 @@ fn test_recreate_listing_different_offerer_fulfilled() {
// check is first order is fulfilled
// assert(order_status.unwrap() == OrderStatus::Fulfilled, 'Order not fulfilled');
- // create a second order over the first one same ressource hash different price, different owner but the previous order is only fulfilled, to cover the case of user who just bought a token to list it instantly but the order is not yet executed
- // we cannot place & cancel a previous order if it's fulfilled
+ // create a second order over the first one same ressource hash different price, different owner
+ // but the previous order is only fulfilled, to cover the case of user who just bought a token
+ // to list it instantly but the order is not yet executed we cannot place & cancel a previous
+ // order if it's fulfilled
let (mut order_listing_2, order_hash_2, _) = setup_listing_order(500000000000000000);
order_listing_2
.offerer = 0x2484a6517b487be8114013f277f9e2010ac001a24a93e3c48cdf5f8f345a81b
@@ -128,8 +132,10 @@ fn test_recreate_listing_same_offerer_fulfilled() {
// check is first order is fulfilled
let order_status = order_status_read(order_hash_1);
assert(order_status.unwrap() == OrderStatus::Fulfilled, 'Order not fulfilled');
- // create a second order over the first one same ressource hash different price, different owner but the previous order is only fulfilled, to cover the case of user who just bought a token to list it instantly but the order is not yet executed
- // we cannot place & cancel a previous order if it's fulfilled
+ // create a second order over the first one same ressource hash different price, different owner
+ // but the previous order is only fulfilled, to cover the case of user who just bought a token
+ // to list it instantly but the order is not yet executed we cannot place & cancel a previous
+ // order if it's fulfilled
let (mut order_listing_2, order_hash_2, _) = setup_listing_order(500000000000000000);
let _ = orderbook::InternalFunctions::_create_listing_order(
ref state, order_listing_2, OrderType::Listing, order_hash_2
@@ -145,7 +151,8 @@ fn test_recreate_listing_new_owner() {
ref state, order_listing_1, OrderType::Listing, order_hash_1
);
- // create a second order over the first one same ressource hash different price, different owner it should work and cancel the previous one
+ // create a second order over the first one same ressource hash different price, different owner
+ // it should work and cancel the previous one
let (mut order_listing_2, order_hash_2, _) = setup_listing_order(500000000000000000);
order_listing_2
.offerer = 0x2584a6517b487be8114013f277f9e2010ac001a24a93e3c48cdf5f8f345a823
@@ -180,7 +187,8 @@ fn test_recreate_listing_same_owner_old_order_expired() {
let _ = orderbook::InternalFunctions::_create_listing_order(
ref state, order_listing_1, OrderType::Listing, order_hash_1
);
- // create a second order over the first one same ressource hash different price, different owner it should work and cancel the previous one
+ // create a second order over the first one same ressource hash different price, different owner
+ // it should work and cancel the previous one
let (order_listing_2, order_hash_2, _) = setup_listing_order(500000000000000000);
let _ = orderbook::InternalFunctions::_create_listing_order(
@@ -211,7 +219,7 @@ fn test_create_offer() {
let contract_address = test_address();
let mut state = orderbook::contract_state_for_testing();
- let mut spy = spy_events(SpyOn::One(contract_address));
+ let mut spy = spy_events();
orderbook::InternalFunctions::_create_offer(
ref state, offer_order, OrderType::Offer, order_hash
@@ -227,11 +235,12 @@ fn test_create_offer() {
@array![
(
contract_address,
- orderbook::Event::OrderPlaced(
- orderbook::OrderPlaced {
+ OrderbookComponent::Event::OrderPlaced(
+ OrderbookComponent::OrderPlaced {
order_hash,
order_version: ORDER_VERSION_V1,
order_type: OrderType::Offer,
+ version: OrderbookComponent::ORDER_PLACED_EVENT_VERSION,
order: offer_order,
cancelled_order_hash: Option::None
}
@@ -244,7 +253,7 @@ fn test_create_offer() {
#[test]
fn test_create_collection_offer() {
let contract_address = test_address();
- let mut spy = spy_events(SpyOn::One(contract_address));
+ let mut spy = spy_events();
let mut offer_order = get_offer_order();
offer_order.token_id = Option::None;
@@ -265,11 +274,12 @@ fn test_create_collection_offer() {
@array![
(
contract_address,
- orderbook::Event::OrderPlaced(
- orderbook::OrderPlaced {
+ OrderbookComponent::Event::OrderPlaced(
+ OrderbookComponent::OrderPlaced {
order_hash,
order_version: ORDER_VERSION_V1,
order_type: OrderType::CollectionOffer,
+ version: OrderbookComponent::ORDER_PLACED_EVENT_VERSION,
order: offer_order,
cancelled_order_hash: Option::None
}
@@ -346,10 +356,10 @@ fn test_fulfill_classic_token_offer() {
let fulfill_broker_address = test_address();
let mut state = orderbook::contract_state_for_testing();
- let mut spy = spy_events(SpyOn::One(contract_address));
+ let mut spy = spy_events();
let order_hash = order_listing.compute_order_hash();
- start_warp(CheatTarget::One(contract_address), order_listing.start_date);
+ cheat_block_timestamp(contract_address, order_listing.start_date, CheatSpan::TargetCalls(1));
let fulfill_info = FulfillInfo {
order_hash,
@@ -362,36 +372,39 @@ fn test_fulfill_classic_token_offer() {
};
orderbook::InternalFunctions::_fulfill_offer(ref state, fulfill_info, order_listing);
+ // FIXME: _fulfill_offer doesn't emit anymore event
+// spy
+// .assert_emitted(
+// @array![
+// (
+// contract_address,
+// OrderbookComponent::Event::OrderFulfilled(
+// OrderbookComponent::OrderFulfilled {
+// order_hash: fulfill_info.order_hash,
+// fulfiller: fulfill_info.fulfiller,
+// related_order_hash: fulfill_info.related_order_hash,
+// order_type: OrderType::Offer,
+// version: OrderbookComponent::ORDER_FULFILLED_EVENT_VERSION,
+// }
+// )
+// )
+// ]
+// );
- spy
- .assert_emitted(
- @array![
- (
- contract_address,
- orderbook::Event::OrderFulfilled(
- orderbook::OrderFulfilled {
- order_hash: fulfill_info.order_hash,
- fulfiller: fulfill_info.fulfiller,
- related_order_hash: Option::None
- }
- )
- )
- ]
- );
}
#[test]
fn test_fulfill_classic_collection_offer() {
let (order_listing, mut order_offer, _, _) = setup_orders();
let contract_address = test_address();
- let mut spy = spy_events(SpyOn::One(contract_address));
+ let mut spy = spy_events();
let mut state = orderbook::contract_state_for_testing();
order_offer.token_id = Option::None;
order_offer.start_date = order_listing.start_date + 100;
order_offer.end_date = order_listing.start_date + 100;
- start_warp(CheatTarget::One(contract_address), order_offer.start_date);
+ cheat_block_timestamp(contract_address, order_listing.start_date, CheatSpan::TargetCalls(1));
let fulfill_info = FulfillInfo {
order_hash: order_listing.compute_order_hash(),
@@ -404,22 +417,24 @@ fn test_fulfill_classic_collection_offer() {
};
orderbook::InternalFunctions::_fulfill_offer(ref state, fulfill_info, order_listing);
-
- spy
- .assert_emitted(
- @array![
- (
- contract_address,
- orderbook::Event::OrderFulfilled(
- orderbook::OrderFulfilled {
- order_hash: fulfill_info.order_hash,
- fulfiller: fulfill_info.fulfiller,
- related_order_hash: Option::None
- }
- )
- )
- ]
- );
+ // FIXME: _fulfill_offer doesn't emit anymore event
+// spy
+// .assert_emitted(
+// @array![
+// (
+// contract_address,
+// OrderbookComponent::Event::OrderFulfilled(
+// OrderbookComponent::OrderFulfilled {
+// order_hash: fulfill_info.order_hash,
+// fulfiller: fulfill_info.fulfiller,
+// related_order_hash: Option::None,
+// order_type: OrderType::CollectionOffer,
+// version: OrderbookComponent::ORDER_FULFILLED_EVENT_VERSION,
+// }
+// )
+// )
+// ]
+// );
}
#[test]
@@ -430,7 +445,9 @@ fn test_fulfill_expired_offer() {
let fulfill_broker_address = test_address();
let mut state = orderbook::contract_state_for_testing();
- start_warp(CheatTarget::One(contract_address), order_listing.end_date + 3600); // +1 hour
+ cheat_block_timestamp(
+ contract_address, order_listing.end_date + 3600, CheatSpan::TargetCalls(1)
+ ); // +1 hour
let fulfill_info = FulfillInfo {
order_hash: order_listing.compute_order_hash(),
diff --git a/contracts/ark_oz/Scarb.lock b/contracts/ark_oz/Scarb.lock
index c3dc71dda..280f9f71c 100644
--- a/contracts/ark_oz/Scarb.lock
+++ b/contracts/ark_oz/Scarb.lock
@@ -11,10 +11,98 @@ dependencies = [
[[package]]
name = "openzeppelin"
-version = "0.10.0"
-source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.10.0#d77082732daab2690ba50742ea41080eb23299d3"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_access",
+ "openzeppelin_account",
+ "openzeppelin_governance",
+ "openzeppelin_introspection",
+ "openzeppelin_presets",
+ "openzeppelin_security",
+ "openzeppelin_token",
+ "openzeppelin_upgrades",
+ "openzeppelin_utils",
+]
+
+[[package]]
+name = "openzeppelin_access"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_introspection",
+ "openzeppelin_utils",
+]
+
+[[package]]
+name = "openzeppelin_account"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_introspection",
+ "openzeppelin_utils",
+]
+
+[[package]]
+name = "openzeppelin_governance"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_access",
+ "openzeppelin_introspection",
+]
+
+[[package]]
+name = "openzeppelin_introspection"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "openzeppelin_presets"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_access",
+ "openzeppelin_account",
+ "openzeppelin_introspection",
+ "openzeppelin_token",
+ "openzeppelin_upgrades",
+]
+
+[[package]]
+name = "openzeppelin_security"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "openzeppelin_token"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+dependencies = [
+ "openzeppelin_account",
+ "openzeppelin_governance",
+ "openzeppelin_introspection",
+]
+
+[[package]]
+name = "openzeppelin_upgrades"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "openzeppelin_utils"
+version = "0.15.1"
+source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.15.1#2f8a93d762858714095a1d391afffa9e21df6983"
+
+[[package]]
+name = "snforge_scarb_plugin"
+version = "0.1.0"
+source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.28.0#4dfe39d96690ed6b3d56971512700de3f58288ea"
[[package]]
name = "snforge_std"
-version = "0.18.0"
-source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.18.0#48f909a56b08cbdc5ca6a21a836b0fbc6c36d55b"
+version = "0.28.0"
+source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.28.0#4dfe39d96690ed6b3d56971512700de3f58288ea"
+dependencies = [
+ "snforge_scarb_plugin",
+]
diff --git a/contracts/ark_oz/Scarb.toml b/contracts/ark_oz/Scarb.toml
index ffa101978..a3c365ee4 100644
--- a/contracts/ark_oz/Scarb.toml
+++ b/contracts/ark_oz/Scarb.toml
@@ -5,9 +5,15 @@ version = "0.1.0"
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
[dependencies]
-starknet = "2.5.4"
-openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" }
-snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.18.0" }
+starknet = "2.7.1"
+openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.15.1" }
+
+[dev-dependencies]
+snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" }
+assert_macros = "0.1.0"
+
+[tool.fmt]
+sort-module-level-items = true
[lib]
diff --git a/contracts/ark_oz/src/erc2981/erc2981.cairo b/contracts/ark_oz/src/erc2981/erc2981.cairo
index 7b0c45b3f..b8eae00c6 100644
--- a/contracts/ark_oz/src/erc2981/erc2981.cairo
+++ b/contracts/ark_oz/src/erc2981/erc2981.cairo
@@ -1,6 +1,8 @@
#[starknet::component]
pub mod ERC2981Component {
use starknet::ContractAddress;
+ use starknet::storage::Map;
+
use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait;
use openzeppelin::introspection::src5::SRC5Component::SRC5Impl;
use openzeppelin::introspection::src5::SRC5Component;
@@ -15,8 +17,8 @@ pub mod ERC2981Component {
struct Storage {
default_receiver: ContractAddress,
default_fees: FeesRatio,
- token_receiver: LegacyMap,
- token_fees: LegacyMap,
+ token_receiver: Map,
+ token_fees: Map,
}
#[event]
diff --git a/contracts/ark_oz/tests/test_erc2981.cairo b/contracts/ark_oz/tests/test_erc2981.cairo
index eed401606..ab066b680 100644
--- a/contracts/ark_oz/tests/test_erc2981.cairo
+++ b/contracts/ark_oz/tests/test_erc2981.cairo
@@ -9,36 +9,31 @@ use ark_oz::erc2981::{IERC2981SetupDispatcher, IERC2981SetupDispatcherTrait};
use ark_oz::erc2981::{FeesRatio, FeesImpl, FeesRatioDefault};
-use snforge_std::{ContractClass, ContractClassTrait, declare};
-use snforge_std::{start_prank, stop_prank, CheatTarget};
+use snforge_std::{ContractClass, ContractClassTrait, declare, DeclareResultTrait, cheat_caller_address, CheatSpan};
fn setup_contract() -> (ContractAddress, ContractAddress, ContractAddress, FeesRatio) {
let owner = contract_address_const::<'owner'>();
let receiver = contract_address_const::<'receiver'>();
let default_fees: FeesRatio = Default::default();
- let contract = declare('MockERC2981');
+ let contract = declare("MockERC2981").unwrap().contract_class();
let mut calldata: Array = array![];
calldata.append(owner.into());
calldata.append(receiver.into());
default_fees.serialize(ref calldata);
-
- (contract.deploy(@calldata).unwrap(), owner, receiver, default_fees)
+ let (mock_erc2981_address, _) = contract.deploy(@calldata).unwrap();
+ (mock_erc2981_address, owner, receiver, default_fees)
}
#[test]
fn test_fees_ratio_invalid() {
- assert!(!FeesRatio { numerator: 0, denominator: 0, }.is_valid(), "Shall be invalid");
-
- assert!(!FeesRatio { numerator: 30, denominator: 30, }.is_valid(), "Shall be invalid");
-
- assert!(!FeesRatio { numerator: 40, denominator: 30, }.is_valid(), "Shall be invalid");
-
- assert!(!FeesRatio { numerator: 5, denominator: 10000, }.is_valid(), "Shall be invalid");
-
- assert!(!FeesRatio { numerator: 0, denominator: 0 }.is_valid(), "Shall be invalid");
-
- assert!(FeesRatio { numerator: 0, denominator: 1, }.is_valid(), "Shall be valid");
+ assert!(!FeesRatio { numerator: 0, denominator: 0, }.is_valid(), "(0,0) Shall be invalid");
+ assert!(!FeesRatio { numerator: 30, denominator: 30, }.is_valid(), "(30,30) Shall be invalid");
+ assert!(!FeesRatio { numerator: 40, denominator: 30, }.is_valid(), "(40,30) Shall be invalid");
+ assert!(FeesRatio { numerator: 5, denominator: 10000, }.is_valid(), "(5,10000) Shall be valid");
+ assert!(!FeesRatio { numerator: 5, denominator: 10001, }.is_valid(), "(5,10001) Shall be invalid");
+ assert!(!FeesRatio { numerator: 0, denominator: 0 }.is_valid(), "(0,0) Shall be invalid");
+ assert!(FeesRatio { numerator: 0, denominator: 1, }.is_valid(), "(0,1) Shall be valid");
}
#[test]
@@ -59,15 +54,14 @@ fn test_erc2981_interface_is_supported() {
}
#[test]
-#[should_panic(expected: ('Caller is not the owner',))]
+#[should_panic(expected: 'Caller is not the owner')]
fn test_only_owner_can_set_default_royalty() {
let (contract_address, _, _, _) = setup_contract();
let alice = contract_address_const::<'alice'>();
let other_receiver = contract_address_const::<'other_receiver'>();
let token = IERC2981SetupDispatcher { contract_address: contract_address };
- start_prank(CheatTarget::One(contract_address), alice);
+ cheat_caller_address(contract_address, alice, CheatSpan::TargetCalls(1));
token.set_default_royalty(other_receiver, Default::default());
- stop_prank(CheatTarget::One(contract_address));
}
// TODO: add event check
@@ -77,9 +71,10 @@ fn test_owner_set_default_royalty() {
let other_receiver = contract_address_const::<'other_receiver'>();
let other_fees = FeesRatio { numerator: 5, denominator: 100, };
let token = IERC2981SetupDispatcher { contract_address: contract_address };
- start_prank(CheatTarget::One(contract_address), owner);
+
+ cheat_caller_address(contract_address, owner, CheatSpan::TargetCalls(1));
token.set_default_royalty(other_receiver, other_fees);
- stop_prank(CheatTarget::One(contract_address));
+
let (receiver, fees_ratio) = token.default_royalty();
assert_eq!(receiver, other_receiver, "Default receiver not updated");
assert_eq!(fees_ratio, other_fees, "Default fees not updated");
@@ -94,9 +89,8 @@ fn test_owner_set_token_royalty() {
let other_fees = FeesRatio { numerator: 5, denominator: 100, };
let token = IERC2981SetupDispatcher { contract_address: contract_address };
- start_prank(CheatTarget::One(contract_address), owner);
+ cheat_caller_address(contract_address, owner, CheatSpan::TargetCalls(1));
token.set_token_royalty(token_id, other_receiver, other_fees);
- stop_prank(CheatTarget::One(contract_address));
let (receiver, fees_ratio) = token.token_royalty(token_id);
assert_eq!(receiver, other_receiver, "Token receiver not updated");
@@ -107,7 +101,7 @@ fn test_owner_set_token_royalty() {
}
#[test]
-#[should_panic(expected: ('Caller is not the owner',))]
+#[should_panic(expected: 'Caller is not the owner')]
fn test_only_owner_can_set_token_royalty() {
let token_id = 256;
let alice = contract_address_const::<'alice'>();
@@ -116,9 +110,8 @@ fn test_only_owner_can_set_token_royalty() {
let other_fees = FeesRatio { numerator: 5, denominator: 100, };
let token = IERC2981SetupDispatcher { contract_address: contract_address };
- start_prank(CheatTarget::One(contract_address), alice);
+ cheat_caller_address(contract_address, alice, CheatSpan::TargetCalls(1));
token.set_token_royalty(token_id, other_receiver, other_fees);
- stop_prank(CheatTarget::One(contract_address));
}
#[test]
@@ -132,10 +125,9 @@ fn test_royalty_compute() {
let other_receiver = contract_address_const::<'other_receiver'>();
let other_fees = FeesRatio { numerator: 5, denominator: 100 };
- start_prank(CheatTarget::One(contract_address), owner);
+ cheat_caller_address(contract_address, owner, CheatSpan::TargetCalls(2));
token.set_token_royalty(token_id, token_receiver, token_fees);
token.set_default_royalty(other_receiver, other_fees);
- stop_prank(CheatTarget::One(contract_address));
let sale_price = 100_000_000;
let token = IERC2981Dispatcher { contract_address: contract_address };
diff --git a/contracts/ark_starknet/Scarb.toml b/contracts/ark_starknet/Scarb.toml
index 7aa7af24b..15134d741 100644
--- a/contracts/ark_starknet/Scarb.toml
+++ b/contracts/ark_starknet/Scarb.toml
@@ -3,16 +3,24 @@ name = "ark_starknet"
version = "0.1.0"
[dependencies]
-starknet = "2.5.4"
-openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" }
-snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.18.0" }
+starknet.workspace = true
+openzeppelin.workspace = true
ark_common = { path = "../ark_common" }
+ark_component = { path = "../ark_component" }
+
ark_tokens = { path = "../ark_tokens" }
ark_oz = { path = "../ark_oz" }
+[dev-dependencies]
+snforge_std.workspace = true
+assert_macros.workspace = true
+
[scripts]
test.workspace = true
+[tool]
+fmt.workspace = true
+
[[target.starknet-contract]]
sierra = true
casm = true
diff --git a/contracts/ark_starknet/src/appchain_messaging.cairo b/contracts/ark_starknet/src/appchain_messaging.cairo
index 37a34eee5..ab899d35f 100644
--- a/contracts/ark_starknet/src/appchain_messaging.cairo
+++ b/contracts/ark_starknet/src/appchain_messaging.cairo
@@ -95,8 +95,9 @@ trait IUpgradeable {
#[starknet::contract]
mod appchain_messaging {
- use starknet::{ContractAddress, ClassHash};
use debug::PrintTrait;
+ use starknet::storage::Map;
+ use starknet::{ContractAddress, ClassHash};
use super::{IAppchainMessaging, IUpgradeable};
@@ -110,10 +111,10 @@ mod appchain_messaging {
// The nonce for messages sent from Starknet.
sn_to_appc_nonce: felt252,
// Ledger of messages hashes sent from Starknet to the appchain.
- sn_to_appc_messages: LegacyMap::,
+ sn_to_appc_messages: Map::,
// Ledger of messages hashes registered from the appchain and a refcount
// associated to it.
- appc_to_sn_messages: LegacyMap::,
+ appc_to_sn_messages: Map::,
}
#[event]
diff --git a/contracts/ark_starknet/src/executor.cairo b/contracts/ark_starknet/src/executor.cairo
index dbb4f8659..5045797b9 100644
--- a/contracts/ark_starknet/src/executor.cairo
+++ b/contracts/ark_starknet/src/executor.cairo
@@ -1,11 +1,11 @@
use ark_common::protocol::order_types::OrderTrait;
+use ark_common::protocol::order_types::OrderType;
+
+use ark_common::protocol::order_v1::{OrderV1, OrderTraitOrderV1};
use core::serde::Serde;
use starknet::ContractAddress;
-use ark_common::protocol::order_v1::{OrderV1, OrderTraitOrderV1};
-use ark_common::protocol::order_types::OrderType;
-
#[derive(Drop, Copy, Debug, Serde, starknet::Store)]
struct OrderInfo {
@@ -54,42 +54,50 @@ impl OrderV1IntoOrderInfo of Into {
#[starknet::contract]
mod executor {
- use core::zeroable::Zeroable;
- use core::traits::Into;
- use starknet::contract_address_to_felt252;
- use starknet::get_contract_address;
-
- use core::debug::PrintTrait;
- use core::traits::TryInto;
- use core::box::BoxTrait;
- use core::option::OptionTrait;
-
- use starknet::{ContractAddress, ClassHash};
use ark_common::protocol::order_types::{
RouteType, ExecutionInfo, ExecutionValidationInfo, FulfillInfo, CreateOrderInfo,
FulfillOrderInfo, CancelOrderInfo, CancelInfo, OrderType,
};
use ark_common::protocol::order_v1::{OrderV1, OrderTraitOrderV1};
+ use ark_component::orderbook::OrderbookComponent;
+ use ark_component::orderbook::{
+ OrderbookHooksCreateOrderEmptyImpl, OrderbookHooksCancelOrderEmptyImpl,
+ OrderbookHooksFulfillOrderEmptyImpl, OrderbookHooksValidateOrderExecutionEmptyImpl,
+ };
use ark_oz::erc2981::interface::IERC2981_ID;
- use ark_oz::erc2981::{IERC2981Dispatcher, IERC2981DispatcherTrait};
use ark_oz::erc2981::{FeesRatio, FeesRatioDefault, FeesImpl};
-
- use ark_starknet::interfaces::{IExecutor, IUpgradable, IMaintenance};
- use ark_starknet::interfaces::FeesAmount;
+ use ark_oz::erc2981::{IERC2981Dispatcher, IERC2981DispatcherTrait};
use ark_starknet::appchain_messaging::{
IAppchainMessagingDispatcher, IAppchainMessagingDispatcherTrait,
};
+ use ark_starknet::interfaces::FeesAmount;
+
+ use ark_starknet::interfaces::{IExecutor, IUpgradable, IMaintenance};
+ use core::box::BoxTrait;
+
+ use core::debug::PrintTrait;
+ use core::option::OptionTrait;
+ use core::traits::Into;
+ use core::traits::TryInto;
+ use core::zeroable::Zeroable;
+ use openzeppelin::introspection::interface::{ISRC5, ISRC5Dispatcher, ISRC5DispatcherTrait};
use openzeppelin::token::{
erc721::interface::{IERC721, IERC721Dispatcher, IERC721DispatcherTrait},
erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}
};
- use openzeppelin::introspection::interface::{ISRC5, ISRC5Dispatcher, ISRC5DispatcherTrait};
+ use starknet::contract_address_to_felt252;
+ use starknet::get_contract_address;
+ use starknet::storage::Map;
+
+ use starknet::{ContractAddress, ClassHash};
use super::{OrderInfo, OrderV1IntoOrderInfo};
+ component!(path: OrderbookComponent, storage: orderbook, event: OrderbookEvent);
+
#[storage]
struct Storage {
admin_address: ContractAddress,
@@ -97,16 +105,18 @@ mod executor {
eth_contract_address: ContractAddress,
messaging_address: ContractAddress,
chain_id: felt252,
- broker_fees: LegacyMap,
+ broker_fees: Map,
ark_fees: FeesRatio,
// order hash -> OrderInfo
- orders: LegacyMap,
+ orders: Map,
// fallback when collection doesn't implement ERC2981
default_receiver: ContractAddress,
default_fees: FeesRatio,
- creator_fees: LegacyMap,
+ creator_fees: Map,
// maintenance mode
in_maintenance: bool,
+ #[substorage(v0)]
+ orderbook: OrderbookComponent::Storage,
}
#[event]
@@ -115,6 +125,8 @@ mod executor {
OrderExecuted: OrderExecuted,
CollectionFallbackFees: CollectionFallbackFees,
ExecutorInMaintenance: ExecutorInMaintenance,
+ // #[flat] // OrderExecuted conflict
+ OrderbookEvent: OrderbookComponent::Event,
}
#[derive(Drop, starknet::Event)]
@@ -147,6 +159,10 @@ mod executor {
const FEES_RATIO_INVALID: felt252 = 'Fees ratio is invalid';
}
+ #[abi(embed_v0)]
+ impl OrderbookImpl = OrderbookComponent::OrderbookImpl;
+ impl OrderbookActionImpl = OrderbookComponent::OrderbookActionImpl;
+
#[constructor]
fn constructor(
ref self: ContractState,
@@ -297,28 +313,11 @@ mod executor {
fn cancel_order(ref self: ContractState, cancelInfo: CancelInfo) {
_ensure_is_not_in_maintenance(@self);
- let messaging = IAppchainMessagingDispatcher {
- contract_address: self.messaging_address.read()
- };
-
- let vinfo = CancelOrderInfo { cancelInfo: cancelInfo.clone() };
-
- let mut vinfo_buf = array![];
- Serde::serialize(@vinfo, ref vinfo_buf);
-
- messaging
- .send_message_to_appchain(
- self.arkchain_orderbook_address.read(),
- selector!("cancel_order_from_l2"),
- vinfo_buf.span(),
- );
+ self.orderbook.cancel_order(cancelInfo);
}
fn create_order(ref self: ContractState, order: OrderV1) {
_ensure_is_not_in_maintenance(@self);
- let messaging = IAppchainMessagingDispatcher {
- contract_address: self.messaging_address.read()
- };
let vinfo = CreateOrderInfo { order: order.clone() };
_verify_create_order(@self, @vinfo);
@@ -327,169 +326,20 @@ mod executor {
let order_info = order.into();
self.orders.write(order_hash, order_info);
- let mut vinfo_buf = array![];
- Serde::serialize(@vinfo, ref vinfo_buf);
-
- messaging
- .send_message_to_appchain(
- self.arkchain_orderbook_address.read(),
- selector!("create_order_from_l2"),
- vinfo_buf.span(),
- );
+ self.orderbook.create_order(order);
}
fn fulfill_order(ref self: ContractState, fulfillInfo: FulfillInfo) {
_ensure_is_not_in_maintenance(@self);
- let messaging = IAppchainMessagingDispatcher {
- contract_address: self.messaging_address.read()
- };
let vinfo = FulfillOrderInfo { fulfillInfo: fulfillInfo.clone() };
_verify_fulfill_order(@self, @vinfo);
- let mut vinfo_buf = array![];
- Serde::serialize(@vinfo, ref vinfo_buf);
-
- messaging
- .send_message_to_appchain(
- self.arkchain_orderbook_address.read(),
- selector!("fulfill_order_from_l2"),
- vinfo_buf.span(),
- );
- }
-
- fn execute_order(ref self: ContractState, execution_info: ExecutionInfo) {
- // assert(
- // starknet::get_caller_address() == self.messaging_address.read(),
- // 'Invalid msg sender'
- // );
-
- // Check if execution_info.currency_contract_address is whitelisted
- _ensure_is_not_in_maintenance(@self);
- assert(
- execution_info.payment_currency_chain_id == self.chain_id.read(),
- 'Chain ID is not SN_MAIN'
- );
-
- let currency_contract = IERC20Dispatcher {
- contract_address: execution_info.payment_currency_address.try_into().unwrap()
- };
-
- let (creator_address, creator_fees_amount) = _compute_creator_fees_amount(
- @self,
- @execution_info.nft_address,
- execution_info.payment_amount,
- execution_info.nft_token_id
- );
- let (fulfill_broker_fees_amount, listing_broker_fees_amount, ark_fees_amount, _) =
- _compute_fees_amount(
- @self,
- execution_info.fulfill_broker_address,
- execution_info.listing_broker_address,
- execution_info.nft_address,
- execution_info.nft_token_id,
- execution_info.payment_amount
- );
- assert!(
- execution_info
- .payment_amount > (fulfill_broker_fees_amount
- + listing_broker_fees_amount
- + creator_fees_amount
- + ark_fees_amount),
- "Fees exceed payment amount"
- );
-
- let seller_amount = execution_info.payment_amount
- - (fulfill_broker_fees_amount
- + listing_broker_fees_amount
- + creator_fees_amount
- + ark_fees_amount);
-
- // split the fees
- currency_contract
- .transfer_from(
- execution_info.payment_from,
- execution_info.fulfill_broker_address,
- fulfill_broker_fees_amount,
- );
-
- currency_contract
- .transfer_from(
- execution_info.payment_from,
- execution_info.listing_broker_address,
- listing_broker_fees_amount
- );
-
- if creator_fees_amount > 0 {
- let (default_receiver_creator, _) = self.get_default_creator_fees();
- if creator_address == default_receiver_creator {
- self
- .emit(
- CollectionFallbackFees {
- collection: execution_info.nft_address,
- amount: creator_fees_amount,
- currency_contract: currency_contract.contract_address,
- receiver: default_receiver_creator,
- }
- )
- }
- currency_contract
- .transfer_from(
- execution_info.payment_from, creator_address, creator_fees_amount
- );
+ match self.orderbook.fulfill_order(fulfillInfo) {
+ Option::Some(execute_info) => { _execute_order(ref self, execute_info); },
+ Option::None => panic!("OB: failed to fulfill order"),
}
-
- if ark_fees_amount > 0 {
- currency_contract
- .transfer_from(
- execution_info.payment_from, self.admin_address.read(), ark_fees_amount
- );
- }
- // finally transfer to the seller
- currency_contract
- .transfer_from(
- execution_info.payment_from, execution_info.payment_to, seller_amount
- );
-
- let nft_contract = IERC721Dispatcher { contract_address: execution_info.nft_address };
- nft_contract
- .transfer_from(
- execution_info.nft_from, execution_info.nft_to, execution_info.nft_token_id
- );
-
- let tx_info = starknet::get_tx_info().unbox();
- let transaction_hash = tx_info.transaction_hash;
- let block_timestamp = starknet::info::get_block_timestamp();
-
- self
- .emit(
- OrderExecuted {
- order_hash: execution_info.order_hash, transaction_hash, block_timestamp,
- }
- );
-
- let messaging = IAppchainMessagingDispatcher {
- contract_address: self.messaging_address.read()
- };
-
- let vinfo = ExecutionValidationInfo {
- order_hash: execution_info.order_hash,
- transaction_hash,
- starknet_block_timestamp: block_timestamp,
- from: execution_info.nft_from,
- to: execution_info.nft_to,
- };
-
- let mut vinfo_buf = array![];
- Serde::serialize(@vinfo, ref vinfo_buf);
-
- messaging
- .send_message_to_appchain(
- self.arkchain_orderbook_address.read(),
- selector!("validate_order_execution"),
- vinfo_buf.span(),
- );
}
}
@@ -747,6 +597,131 @@ mod executor {
)
}
+ fn _execute_order(ref self: ContractState, execution_info: ExecutionInfo) {
+ // assert(
+ // starknet::get_caller_address() == self.messaging_address.read(),
+ // 'Invalid msg sender'
+ // );
+
+ // Check if execution_info.currency_contract_address is whitelisted
+ _ensure_is_not_in_maintenance(@self);
+ assert(
+ execution_info.payment_currency_chain_id == self.chain_id.read(),
+ 'Chain ID is not SN_MAIN'
+ );
+
+ let currency_contract = IERC20Dispatcher {
+ contract_address: execution_info.payment_currency_address.try_into().unwrap()
+ };
+
+ let (creator_address, creator_fees_amount) = _compute_creator_fees_amount(
+ @self,
+ @execution_info.nft_address,
+ execution_info.payment_amount,
+ execution_info.nft_token_id
+ );
+ let (fulfill_broker_fees_amount, listing_broker_fees_amount, ark_fees_amount, _) =
+ _compute_fees_amount(
+ @self,
+ execution_info.fulfill_broker_address,
+ execution_info.listing_broker_address,
+ execution_info.nft_address,
+ execution_info.nft_token_id,
+ execution_info.payment_amount
+ );
+ assert!(
+ execution_info
+ .payment_amount > (fulfill_broker_fees_amount
+ + listing_broker_fees_amount
+ + creator_fees_amount
+ + ark_fees_amount),
+ "Fees exceed payment amount"
+ );
+
+ let seller_amount = execution_info.payment_amount
+ - (fulfill_broker_fees_amount
+ + listing_broker_fees_amount
+ + creator_fees_amount
+ + ark_fees_amount);
+
+ // split the fees
+ if fulfill_broker_fees_amount > 0 {
+ currency_contract
+ .transfer_from(
+ execution_info.payment_from,
+ execution_info.fulfill_broker_address,
+ fulfill_broker_fees_amount,
+ );
+ }
+
+ if listing_broker_fees_amount > 0 {
+ currency_contract
+ .transfer_from(
+ execution_info.payment_from,
+ execution_info.listing_broker_address,
+ listing_broker_fees_amount
+ );
+ }
+
+ if creator_fees_amount > 0 {
+ let (default_receiver_creator, _) = self.get_default_creator_fees();
+ if creator_address == default_receiver_creator {
+ self
+ .emit(
+ CollectionFallbackFees {
+ collection: execution_info.nft_address,
+ amount: creator_fees_amount,
+ currency_contract: currency_contract.contract_address,
+ receiver: default_receiver_creator,
+ }
+ )
+ }
+ currency_contract
+ .transfer_from(execution_info.payment_from, creator_address, creator_fees_amount);
+ }
+
+ if ark_fees_amount > 0 {
+ currency_contract
+ .transfer_from(
+ execution_info.payment_from, self.admin_address.read(), ark_fees_amount
+ );
+ }
+ // finally transfer to the seller
+ if seller_amount > 0 {
+ currency_contract
+ .transfer_from(
+ execution_info.payment_from, execution_info.payment_to, seller_amount
+ );
+ }
+
+ let nft_contract = IERC721Dispatcher { contract_address: execution_info.nft_address };
+ nft_contract
+ .transfer_from(
+ execution_info.nft_from, execution_info.nft_to, execution_info.nft_token_id
+ );
+
+ let tx_info = starknet::get_tx_info().unbox();
+ let transaction_hash = tx_info.transaction_hash;
+ let block_timestamp = starknet::info::get_block_timestamp();
+
+ self
+ .emit(
+ OrderExecuted {
+ order_hash: execution_info.order_hash, transaction_hash, block_timestamp,
+ }
+ );
+
+ let vinfo = ExecutionValidationInfo {
+ order_hash: execution_info.order_hash,
+ transaction_hash,
+ starknet_block_timestamp: block_timestamp,
+ from: execution_info.nft_from,
+ to: execution_info.nft_to,
+ };
+
+ self.orderbook.validate_order_execution(vinfo);
+ }
+
fn _check_erc20_amount(
token_address: @ContractAddress, amount: u256, user: @ContractAddress
) -> bool {
diff --git a/contracts/ark_starknet/src/interfaces.cairo b/contracts/ark_starknet/src/interfaces.cairo
index ed0b1c60a..98fb2264e 100644
--- a/contracts/ark_starknet/src/interfaces.cairo
+++ b/contracts/ark_starknet/src/interfaces.cairo
@@ -1,9 +1,9 @@
-//! Interfaces for arkchain operator.
-use starknet::{ClassHash, ContractAddress};
use ark_common::protocol::order_types::ExecutionInfo;
use ark_common::protocol::order_types::OrderV1;
use ark_common::protocol::order_types::{FulfillInfo, CancelInfo};
use ark_oz::erc2981::FeesRatio;
+//! Interfaces for arkchain operator.
+use starknet::{ClassHash, ContractAddress};
#[derive(Serde, Drop)]
struct FeesAmount {
@@ -18,7 +18,7 @@ trait IExecutor {
fn fulfill_order(ref self: T, fulfillInfo: FulfillInfo);
fn cancel_order(ref self: T, cancelInfo: CancelInfo);
fn create_order(ref self: T, order: OrderV1);
- fn execute_order(ref self: T, execution_info: ExecutionInfo);
+ // fn execute_order(ref self: T, execution_info: ExecutionInfo);
fn update_admin_address(ref self: T, admin_address: ContractAddress);
fn update_orderbook_address(ref self: T, orderbook_address: ContractAddress);
fn update_eth_address(ref self: T, eth_address: ContractAddress);
diff --git a/contracts/ark_starknet/src/lib.cairo b/contracts/ark_starknet/src/lib.cairo
index 57f65f841..ac8cf351f 100644
--- a/contracts/ark_starknet/src/lib.cairo
+++ b/contracts/ark_starknet/src/lib.cairo
@@ -1,4 +1,4 @@
mod appchain_messaging;
-mod interfaces;
mod executor;
+mod interfaces;
diff --git a/contracts/ark_starknet/tests/common/setup.cairo b/contracts/ark_starknet/tests/common/setup.cairo
index e2251c8a4..d4e957967 100644
--- a/contracts/ark_starknet/tests/common/setup.cairo
+++ b/contracts/ark_starknet/tests/common/setup.cairo
@@ -1,13 +1,13 @@
+use ark_common::protocol::order_types::RouteType;
+
+use ark_common::protocol::order_v1::OrderV1;
use serde::Serde;
+use snforge_std::{ContractClass, ContractClassTrait, declare, DeclareResultTrait};
use starknet::{ContractAddress, contract_address_const};
-use snforge_std::{ContractClass, ContractClassTrait, declare};
-
-use ark_common::protocol::order_v1::OrderV1;
-use ark_common::protocol::order_types::RouteType;
fn deploy_erc20() -> ContractAddress {
- let contract = declare('FreeMintERC20');
+ let contract = declare("FreeMintERC20").unwrap().contract_class();
let initial_supply: u256 = 10_000_000_000_u256;
let name: ByteArray = "DummyERC20";
let symbol: ByteArray = "DUMMY";
@@ -16,7 +16,7 @@ fn deploy_erc20() -> ContractAddress {
initial_supply.serialize(ref calldata);
name.serialize(ref calldata);
symbol.serialize(ref calldata);
- let erc20_address = contract.deploy(@calldata).unwrap();
+ let (erc20_address, _) = contract.deploy(@calldata).unwrap();
erc20_address
}
@@ -29,27 +29,28 @@ fn deploy_nft(royalty: bool) -> ContractAddress {
name.serialize(ref calldata);
symbol.serialize(ref calldata);
base_uri.serialize(ref calldata);
- if royalty {
+ let (nft_address, _) = if royalty {
let owner = contract_address_const::<'nft_owner'>();
calldata.append(owner.into());
- let contract = declare('FreeMintNFTRoyalty');
+ let contract = declare("FreeMintNFTRoyalty").unwrap().contract_class();
contract.deploy(@calldata).unwrap()
} else {
- let contract = declare('FreeMintNFT');
+ let contract = declare("FreeMintNFT").unwrap().contract_class();
contract.deploy(@calldata).unwrap()
- }
+ };
+ nft_address
}
fn deploy_executor() -> ContractAddress {
- let messaging_contract = declare('appchain_messaging');
+ let messaging_contract = declare("appchain_messaging").unwrap().contract_class();
let messaging_owner = contract_address_const::<'messaging_owner'>();
let appchain_account = contract_address_const::<'messaging_account'>();
let mut messaging_calldata: Array = array![];
messaging_calldata.append(messaging_owner.into());
messaging_calldata.append(appchain_account.into());
- let messaging_address = messaging_contract.deploy(@messaging_calldata).unwrap();
+ let (messaging_address, _) = messaging_contract.deploy(@messaging_calldata).unwrap();
- let contract = declare('executor');
+ let contract = declare("executor").unwrap().contract_class();
let admin_address = contract_address_const::<'admin'>();
let eth_address = contract_address_const::<'eth'>();
@@ -58,7 +59,8 @@ fn deploy_executor() -> ContractAddress {
calldata.append(eth_address.into());
calldata.append(messaging_address.into());
calldata.append('SN_MAIN');
- contract.deploy(@calldata).unwrap()
+ let (executor_address, _) = contract.deploy(@calldata).unwrap();
+ executor_address
}
fn setup() -> (ContractAddress, ContractAddress, ContractAddress) {
diff --git a/contracts/ark_starknet/tests/integration/create_order.cairo b/contracts/ark_starknet/tests/integration/create_order.cairo
index a41a08368..7c7626cfa 100644
--- a/contracts/ark_starknet/tests/integration/create_order.cairo
+++ b/contracts/ark_starknet/tests/integration/create_order.cairo
@@ -1,7 +1,6 @@
-use starknet::{ContractAddress, contract_address_const};
+use ark_common::protocol::order_types::RouteType;
use ark_common::protocol::order_v1::OrderV1;
-use ark_common::protocol::order_types::RouteType;
use ark_starknet::interfaces::{
@@ -14,8 +13,8 @@ use ark_tokens::erc20::IFreeMintDispatcherTrait as Erc20DispatcherTrait;
use ark_tokens::erc721::IFreeMintDispatcher as Erc721Dispatcher;
use ark_tokens::erc721::IFreeMintDispatcherTrait as Erc721DispatcherTrait;
-use snforge_std as snf;
-use snf::{ContractClass, ContractClassTrait, CheatTarget};
+use snforge_std::{cheat_caller_address, CheatSpan};
+use starknet::{ContractAddress, contract_address_const};
use super::super::common::setup::{setup, setup_order};
@@ -32,9 +31,8 @@ fn test_create_order_erc20_to_erc721_ok() {
order.offerer = offerer;
order.start_amount = start_amount;
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
@@ -52,14 +50,13 @@ fn test_create_order_erc721_to_erc20_ok() {
order.offerer = offerer;
order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Caller is not the offerer",))]
+#[should_panic(expected: "Caller is not the offerer")]
fn test_create_order_offerer_shall_be_caller() {
let (executor_address, erc20_address, nft_address) = setup();
let offerer = contract_address_const::<'offerer'>();
@@ -68,13 +65,12 @@ fn test_create_order_offerer_shall_be_caller() {
let mut order = setup_order(erc20_address, nft_address);
order.offerer = offerer;
- snf::start_prank(CheatTarget::One(executor_address), caller);
+ cheat_caller_address(executor_address, caller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Offerer does not own enough ERC20 tokens",))]
+#[should_panic(expected: "Offerer does not own enough ERC20 tokens")]
fn test_create_order_offerer_not_enough_erc20_tokens() {
let (executor_address, erc20_address, nft_address) = setup();
let offerer = contract_address_const::<'offerer'>();
@@ -87,13 +83,12 @@ fn test_create_order_offerer_not_enough_erc20_tokens() {
order.offerer = offerer;
order.start_amount = start_amount;
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Offerer does not own the specified ERC721 token",))]
+#[should_panic(expected: "Offerer does not own the specified ERC721 token")]
fn test_create_order_offerer_not_own_ec721_token() {
let (executor_address, erc20_address, nft_address) = setup();
let offerer = contract_address_const::<'offerer'>();
@@ -109,13 +104,12 @@ fn test_create_order_offerer_not_own_ec721_token() {
order.offerer = offerer;
order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ('Executor not enabled',))]
+#[should_panic(expected: 'Executor not enabled')]
fn test_create_order_erc20_to_erc721_disabled() {
let (executor_address, erc20_address, nft_address) = setup();
let admin = contract_address_const::<'admin'>();
@@ -128,17 +122,15 @@ fn test_create_order_erc20_to_erc721_disabled() {
order.offerer = offerer;
order.start_amount = start_amount;
- snf::start_prank(CheatTarget::One(executor_address), admin);
+ cheat_caller_address(executor_address, admin, CheatSpan::TargetCalls(1));
IMaintenanceDispatcher { contract_address: executor_address }.set_maintenance_mode(true);
- snf::stop_prank(CheatTarget::One(executor_address));
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ('Executor not enabled',))]
+#[should_panic(expected: 'Executor not enabled')]
fn test_create_order_erc721_to_erc20_disabled() {
let (executor_address, erc20_address, nft_address) = setup();
let admin = contract_address_const::<'admin'>();
@@ -154,11 +146,9 @@ fn test_create_order_erc721_to_erc20_disabled() {
order.offerer = offerer;
order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor_address), admin);
+ cheat_caller_address(executor_address, admin, CheatSpan::TargetCalls(1));
IMaintenanceDispatcher { contract_address: executor_address }.set_maintenance_mode(true);
- snf::stop_prank(CheatTarget::One(executor_address));
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
}
diff --git a/contracts/ark_starknet/tests/integration/execute_order.cairo b/contracts/ark_starknet/tests/integration/execute_order.cairo
index 1fee4bad2..942c83b5a 100644
--- a/contracts/ark_starknet/tests/integration/execute_order.cairo
+++ b/contracts/ark_starknet/tests/integration/execute_order.cairo
@@ -1,9 +1,5 @@
-use starknet::{ContractAddress, contract_address_const};
-
-use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
-use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait};
-use ark_common::protocol::order_v1::OrderV1;
use ark_common::protocol::order_types::{FulfillInfo, ExecutionInfo, OrderTrait, RouteType};
+use ark_common::protocol::order_v1::OrderV1;
use ark_oz::erc2981::{IERC2981SetupDispatcher, IERC2981SetupDispatcherTrait};
@@ -16,10 +12,14 @@ use ark_tokens::erc20::{IFreeMintDispatcher, IFreeMintDispatcherTrait};
use ark_tokens::erc721::IFreeMintDispatcher as Erc721Dispatcher;
use ark_tokens::erc721::IFreeMintDispatcherTrait as Erc721DispatcherTrait;
-use snforge_std as snf;
-use snf::{cheatcodes::{events::EventFetcher, events::EventAssertions,}, event_name_hash,};
-use snf::{ContractClass, ContractClassTrait, CheatTarget, spy_events, SpyOn};
+use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
+use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait};
+use snforge_std::{
+ ContractClass, ContractClassTrait, cheat_caller_address, CheatSpan, spy_events,
+ EventSpyAssertionsTrait, EventSpyTrait, Event, EventsFilterTrait,
+};
+use starknet::{ContractAddress, contract_address_const};
use super::super::common::setup::{setup, setup_order, setup_royalty};
fn create_fulfill_info(
@@ -93,25 +93,21 @@ fn setup_execute_order(
order.start_amount = start_amount;
order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor.contract_address), offerer);
+ cheat_caller_address(executor.contract_address, offerer, CheatSpan::TargetCalls(1));
executor.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
let order_hash = order.compute_order_hash();
- snf::start_prank(CheatTarget::One(erc20_address), offerer);
+ cheat_caller_address(erc20_address, offerer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(nft_address), fulfiller);
+ cheat_caller_address(nft_address, fulfiller, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
executor.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
let execution_info = create_execution_info(
order_hash,
@@ -167,13 +163,11 @@ fn test_execute_order_check_brokers_fees_ok() {
let fulfill_fees_ratio = FeesRatio { numerator: 10, denominator: 100 };
let listing_fees_ratio = FeesRatio { numerator: 5, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
assert_eq!(
executor.get_broker_fees(fulfill_broker),
@@ -218,17 +212,14 @@ fn test_execute_order_check_ark_fees_ok() {
let listing_fees_ratio = FeesRatio { numerator: 5, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), admin_address);
+ cheat_caller_address(executor.contract_address, admin_address, CheatSpan::TargetCalls(1));
executor.set_ark_fees(ark_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
let admin_balance = erc20.balance_of(admin_address);
let admin_delta = 50_000; // 0.5%
@@ -264,13 +255,11 @@ fn test_execute_order_erc2981_default_royalty_check_fees_ok() {
let listing_fees_ratio = FeesRatio { numerator: 5, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
assert_eq!(
executor.get_broker_fees(fulfill_broker),
@@ -298,14 +287,13 @@ fn test_execute_order_erc2981_default_royalty_check_fees_ok() {
- listing_broker_delta
- creator_delta;
- snf::start_prank(CheatTarget::One(nft_address), nft_owner);
+ cheat_caller_address(nft_address, nft_owner, CheatSpan::TargetCalls(1));
IERC2981SetupDispatcher { contract_address: nft_address }
.set_default_royalty(creator, FeesRatio { numerator: 2, denominator: 100 });
- snf::stop_prank(CheatTarget::One(nft_address));
- let mut spy = spy_events(SpyOn::One(executor_address));
+ let mut spy = spy_events();
IExecutorDispatcher { contract_address: executor_address }.execute_order(execution_info);
- spy.fetch_events();
+ let events = spy.get_events().emitted_by(executor_address);
assert_eq!(
erc20.balance_of(fulfill_broker) - fulfill_broker_balance,
@@ -329,10 +317,10 @@ fn test_execute_order_erc2981_default_royalty_check_fees_ok() {
"Fulfiller balance not correct"
);
- assert_eq!(spy.events.len(), 1, "Expected 1 events");
- let (_, event) = spy.events.at(0);
+ assert_eq!(events.events.len(), 1, "Expected 1 events");
+ let (_, event) = events.events.at(0);
assert_eq!(event.keys.len(), 3, "There should be 3 keys");
- assert!(event.keys.at(0) == @event_name_hash('OrderExecuted'), "Wrong event name");
+ assert_eq!(event.keys.at(0), @selector!("OrderExecuted"), "Wrong event name");
}
#[test]
@@ -360,13 +348,11 @@ fn test_execute_order_erc2981_token_royalty_check_fees_ok() {
let listing_fees_ratio = FeesRatio { numerator: 5, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
assert_eq!(
executor.get_broker_fees(fulfill_broker),
@@ -398,14 +384,13 @@ fn test_execute_order_erc2981_token_royalty_check_fees_ok() {
- creator_delta
- default_creator_delta;
- snf::start_prank(CheatTarget::One(nft_address), nft_owner);
+ cheat_caller_address(nft_address, nft_owner, CheatSpan::TargetCalls(2));
IERC2981SetupDispatcher { contract_address: nft_address }
.set_token_royalty(
execution_info.nft_token_id, creator, FeesRatio { numerator: 3, denominator: 100 }
);
IERC2981SetupDispatcher { contract_address: nft_address }
.set_default_royalty(default_creator, FeesRatio { numerator: 2, denominator: 100 });
- snf::stop_prank(CheatTarget::One(nft_address));
IExecutorDispatcher { contract_address: executor_address }.execute_order(execution_info);
assert_eq!(
@@ -459,12 +444,11 @@ fn test_execute_order_non_erc2981_default_royalty_check_fees_ok() {
let listing_fees_ratio = FeesRatio { numerator: 5, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
assert_eq!(
executor.get_broker_fees(fulfill_broker),
@@ -492,17 +476,16 @@ fn test_execute_order_non_erc2981_default_royalty_check_fees_ok() {
- listing_broker_delta
- creator_delta;
- snf::start_prank(CheatTarget::One(executor.contract_address), admin_address);
+ cheat_caller_address(executor.contract_address, admin_address, CheatSpan::TargetCalls(2));
executor.set_default_creator_fees(creator, FeesRatio { numerator: 2, denominator: 100 });
executor
.set_collection_creator_fees(
fake_nft_address, creator, FeesRatio { numerator: 4, denominator: 1000 }
);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- let mut spy = spy_events(SpyOn::One(executor_address));
+ let mut spy = spy_events();
IExecutorDispatcher { contract_address: executor_address }.execute_order(execution_info);
- spy.fetch_events();
+ let events = spy.get_events().emitted_by(executor_address);
assert_eq!(
erc20.balance_of(fulfill_broker) - fulfill_broker_balance,
@@ -526,16 +509,16 @@ fn test_execute_order_non_erc2981_default_royalty_check_fees_ok() {
"Fulfiller balance not correct"
);
- assert_eq!(spy.events.len(), 2, "Expected 2 events");
- let (_, event) = spy.events.at(0);
+ assert_eq!(events.events.len(), 2, "Expected 2 events");
+ let (_, event) = events.events.at(0);
assert_eq!(event.keys.len(), 4, "There should be 4 keys");
- assert_eq!(@event_name_hash('CollectionFallbackFees'), event.keys.at(0), "Wrong event name");
+ assert_eq!(@selector!("CollectionFallbackFees"), event.keys.at(0), "Wrong event name");
assert_eq!(nft_address, (*event.keys.at(1)).try_into().unwrap(), "Wrong collection address");
assert_eq!(creator_delta.low, (*event.keys.at(2)).try_into().unwrap(), "Wrong low amount");
assert_eq!(creator_delta.high, (*event.keys.at(3)).try_into().unwrap(), "Wrong high amount");
- let (_, event) = spy.events.at(1);
+ let (_, event) = events.events.at(1);
assert_eq!(event.keys.len(), 3, "There should be 3 keys");
- assert_eq!(@event_name_hash('OrderExecuted'), event.keys.at(0), "Wrong event name");
+ assert_eq!(@selector!("OrderExecuted"), event.keys.at(0), "Wrong event name");
}
#[test]
@@ -560,12 +543,11 @@ fn test_execute_order_non_erc2981_collection_royalty_check_fees_ok() {
let listing_fees_ratio = FeesRatio { numerator: 5, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
assert_eq!(
executor.get_broker_fees(fulfill_broker),
@@ -593,13 +575,12 @@ fn test_execute_order_non_erc2981_collection_royalty_check_fees_ok() {
- listing_broker_delta
- creator_delta;
- snf::start_prank(CheatTarget::One(executor.contract_address), admin_address);
+ cheat_caller_address(executor.contract_address, admin_address, CheatSpan::TargetCalls(2));
executor.set_default_creator_fees(other_creator, FeesRatio { numerator: 4, denominator: 100 });
executor
.set_collection_creator_fees(
nft_address, creator, FeesRatio { numerator: 2, denominator: 100 }
);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
IExecutorDispatcher { contract_address: executor_address }.execute_order(execution_info);
assert_eq!(
@@ -626,7 +607,7 @@ fn test_execute_order_non_erc2981_collection_royalty_check_fees_ok() {
}
#[test]
-#[should_panic(expected: ("Fees exceed payment amount",))]
+#[should_panic(expected: "Fees exceed payment amount")]
fn test_execute_order_check_fee_too_much_fees() {
let fulfiller = contract_address_const::<'fulfiller'>();
let listing_broker = contract_address_const::<'listing_broker'>();
@@ -646,12 +627,11 @@ fn test_execute_order_check_fee_too_much_fees() {
let listing_fees_ratio = FeesRatio { numerator: 60, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
IExecutorDispatcher { contract_address: executor_address }.execute_order(execution_info);
assert_eq!(erc20.balance_of(fulfill_broker), 1_000_000, "Fulfill broker balance not correct");
@@ -659,7 +639,7 @@ fn test_execute_order_check_fee_too_much_fees() {
}
#[test]
-#[should_panic(expected: ('Executor not enabled',))]
+#[should_panic(expected: 'Executor not enabled')]
fn test_execute_order_disabled() {
let fulfiller = contract_address_const::<'fulfiller'>();
let listing_broker = contract_address_const::<'listing_broker'>();
@@ -672,9 +652,8 @@ fn test_execute_order_disabled() {
admin_address, offerer, fulfiller, listing_broker, fulfill_broker, start_amount, false
);
- snf::start_prank(CheatTarget::One(executor_address), admin_address);
+ cheat_caller_address(executor_address, admin_address, CheatSpan::TargetCalls(1));
IMaintenanceDispatcher { contract_address: executor_address }.set_maintenance_mode(true);
- snf::stop_prank(CheatTarget::One(executor_address));
IExecutorDispatcher { contract_address: executor_address }.execute_order(execution_info);
}
diff --git a/contracts/ark_starknet/tests/integration/fees_amount.cairo b/contracts/ark_starknet/tests/integration/fees_amount.cairo
index 23d90c4ec..1000e32ba 100644
--- a/contracts/ark_starknet/tests/integration/fees_amount.cairo
+++ b/contracts/ark_starknet/tests/integration/fees_amount.cairo
@@ -1,12 +1,9 @@
-use starknet::{ContractAddress, contract_address_const};
-
use ark_starknet::interfaces::{
IExecutorDispatcher, IExecutorDispatcherTrait, FeesAmount, FeesRatio
};
-use snforge_std as snf;
-use snf::{ContractClass, ContractClassTrait, CheatTarget};
-
+use snforge_std::{cheat_caller_address, CheatSpan};
+use starknet::{ContractAddress, contract_address_const};
use super::super::common::setup::setup;
@@ -26,18 +23,15 @@ fn test_get_fees_amount_default_creator() {
let ark_fees_ratio = FeesRatio { numerator: 1, denominator: 100 };
let default_creator_fees_ratio = FeesRatio { numerator: 2, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), admin);
+ cheat_caller_address(executor.contract_address, admin, CheatSpan::TargetCalls(2));
executor.set_ark_fees(ark_fees_ratio);
executor.set_default_creator_fees(creator, default_creator_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
let fees_amount = executor
.get_fees_amount(fulfill_broker, listing_broker, nft_address, 1, amount);
@@ -65,19 +59,16 @@ fn test_get_fees_amount_collection_creator() {
let default_creator_fees_ratio = FeesRatio { numerator: 2, denominator: 100 };
let collection_creator_fees_ratio = FeesRatio { numerator: 3, denominator: 100 };
- snf::start_prank(CheatTarget::One(executor.contract_address), fulfill_broker);
+ cheat_caller_address(executor.contract_address, fulfill_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(fulfill_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), listing_broker);
+ cheat_caller_address(executor.contract_address, listing_broker, CheatSpan::TargetCalls(1));
executor.set_broker_fees(listing_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
- snf::start_prank(CheatTarget::One(executor.contract_address), admin);
+ cheat_caller_address(executor.contract_address, admin, CheatSpan::TargetCalls(3));
executor.set_ark_fees(ark_fees_ratio);
executor.set_default_creator_fees(creator, default_creator_fees_ratio);
executor.set_collection_creator_fees(nft_address, creator, collection_creator_fees_ratio);
- snf::stop_prank(CheatTarget::One(executor.contract_address));
let fees_amount = executor
.get_fees_amount(fulfill_broker, listing_broker, nft_address, 1, amount);
diff --git a/contracts/ark_starknet/tests/integration/fulfill_order.cairo b/contracts/ark_starknet/tests/integration/fulfill_order.cairo
index f2b23abe4..72ec091d8 100644
--- a/contracts/ark_starknet/tests/integration/fulfill_order.cairo
+++ b/contracts/ark_starknet/tests/integration/fulfill_order.cairo
@@ -1,9 +1,5 @@
-use starknet::{ContractAddress, contract_address_const};
-
-use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
-use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait};
-use ark_common::protocol::order_v1::OrderV1;
use ark_common::protocol::order_types::{FulfillInfo, OrderTrait, RouteType};
+use ark_common::protocol::order_v1::OrderV1;
use ark_starknet::interfaces::{
@@ -15,8 +11,11 @@ use ark_tokens::erc20::{IFreeMintDispatcher, IFreeMintDispatcherTrait};
use ark_tokens::erc721::IFreeMintDispatcher as Erc721Dispatcher;
use ark_tokens::erc721::IFreeMintDispatcherTrait as Erc721DispatcherTrait;
-use snforge_std as snf;
-use snf::{ContractClass, ContractClassTrait, CheatTarget};
+use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
+use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait};
+
+use snforge_std::{cheat_caller_address, CheatSpan};
+use starknet::{ContractAddress, contract_address_const};
use super::super::common::setup::{setup, setup_order};
@@ -36,9 +35,8 @@ fn create_offer_order(
order.start_amount = start_amount;
order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
(order.compute_order_hash(), offerer, start_amount)
}
@@ -56,9 +54,8 @@ fn create_collection_offer_order(
order.start_amount = start_amount;
order.token_id = Option::None;
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
(order.compute_order_hash(), offerer, start_amount)
}
@@ -82,9 +79,8 @@ fn create_listing_order(
order.token_id = Option::Some(token_id);
order.start_amount = start_amount;
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
(order.compute_order_hash(), offerer, token_id)
}
@@ -110,9 +106,8 @@ fn create_auction_order(
order.start_amount = start_amount;
order.end_amount = end_amount;
- snf::start_prank(CheatTarget::One(executor_address), offerer);
+ cheat_caller_address(executor_address, offerer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(order);
- snf::stop_prank(CheatTarget::One(executor_address));
(order.compute_order_hash(), offerer, token_id)
}
@@ -144,20 +139,17 @@ fn test_fulfill_offer_order_ok() {
executor_address, erc20_address, nft_address, token_id
);
- snf::start_prank(CheatTarget::One(erc20_address), offerer);
+ cheat_caller_address(erc20_address, offerer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(nft_address), fulfiller);
+ cheat_caller_address(nft_address, fulfiller, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
@@ -172,24 +164,21 @@ fn test_fulfill_listing_order_ok() {
IFreeMintDispatcher { contract_address: erc20_address }.mint(fulfiller, start_amount);
- snf::start_prank(CheatTarget::One(nft_address), offerer);
+ cheat_caller_address(nft_address, offerer, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(erc20_address), fulfiller);
+ cheat_caller_address(erc20_address, fulfiller, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Caller is not the fulfiller",))]
+#[should_panic(expected: "Caller is not the fulfiller")]
fn test_fulfill_order_fulfiller_shall_be_caller() {
let (executor_address, _erc20_address, nft_address) = setup();
let caller = contract_address_const::<'caller'>();
@@ -197,13 +186,12 @@ fn test_fulfill_order_fulfiller_shall_be_caller() {
let fulfill_info = create_fulfill_info(0x123, fulfiller, nft_address, 1);
- snf::start_prank(CheatTarget::One(executor_address), caller);
+ cheat_caller_address(executor_address, caller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Fulfiller does not own enough ERC20 tokens",))]
+#[should_panic(expected: "Fulfiller does not own enough ERC20 tokens")]
fn test_fulfill_listing_order_fulfiller_not_enough_erc20_token() {
let (executor_address, erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
@@ -217,13 +205,12 @@ fn test_fulfill_listing_order_fulfiller_not_enough_erc20_token() {
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Fulfiller does not own the specified ERC721 token",))]
+#[should_panic(expected: "Fulfiller does not own the specified ERC721 token")]
fn test_fulfill_offer_order_fulfiller_not_owner() {
let (executor_address, erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
@@ -239,26 +226,24 @@ fn test_fulfill_offer_order_fulfiller_not_owner() {
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Order not found",))]
+#[should_panic(expected: "Order not found")]
fn test_fulfill_order_not_found() {
let (executor_address, _erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
let fulfill_info = create_fulfill_info(0x1234, fulfiller, nft_address, 1);
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Offerer's allowance of executor is not enough",))]
+#[should_panic(expected: "Offerer's allowance of executor is not enough")]
fn test_fulfill_offer_order_offerer_not_enough_allowance() {
let (executor_address, erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
@@ -271,25 +256,22 @@ fn test_fulfill_offer_order_offerer_not_enough_allowance() {
executor_address, erc20_address, nft_address, token_id
);
- snf::start_prank(CheatTarget::One(erc20_address), offerer);
+ cheat_caller_address(erc20_address, offerer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }
.approve(executor_address, start_amount - 10);
- snf::stop_prank(CheatTarget::One(erc20_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(nft_address), fulfiller);
+ cheat_caller_address(nft_address, fulfiller, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Fulfiller's allowance of executor is not enough",))]
+#[should_panic(expected: "Fulfiller's allowance of executor is not enough")]
fn test_fulfill_listing_order_fulfiller_not_enough_allowance() {
let (executor_address, erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
@@ -301,25 +283,22 @@ fn test_fulfill_listing_order_fulfiller_not_enough_allowance() {
IFreeMintDispatcher { contract_address: erc20_address }.mint(fulfiller, start_amount);
- snf::start_prank(CheatTarget::One(nft_address), offerer);
+ cheat_caller_address(nft_address, offerer, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(erc20_address), fulfiller);
+ cheat_caller_address(erc20_address, fulfiller, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }
.approve(executor_address, start_amount - 10);
- snf::stop_prank(CheatTarget::One(erc20_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Executor not approved by offerer",))]
+#[should_panic(expected: "Executor not approved by offerer")]
fn test_fulfill_listing_order_offerer_not_approved() {
let (executor_address, erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
@@ -333,17 +312,15 @@ fn test_fulfill_listing_order_offerer_not_approved() {
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(erc20_address), fulfiller);
+ cheat_caller_address(erc20_address, fulfiller, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Executor not approved by fulfiller",))]
+#[should_panic(expected: "Executor not approved by fulfiller")]
fn test_fulfill_offer_order_fulfiller_not_approved() {
let (executor_address, erc20_address, nft_address) = setup();
let fulfiller = contract_address_const::<'fulfiller'>();
@@ -356,20 +333,18 @@ fn test_fulfill_offer_order_fulfiller_not_approved() {
executor_address, erc20_address, nft_address, token_id
);
- snf::start_prank(CheatTarget::One(erc20_address), offerer);
+ cheat_caller_address(erc20_address, offerer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Offerer and fulfiller must be different",))]
+#[should_panic(expected: "Offerer and fulfiller must be different")]
fn test_fulfill_offer_order_fulfiller_same_as_offerer() {
let (executor_address, erc20_address, nft_address) = setup();
@@ -382,24 +357,21 @@ fn test_fulfill_offer_order_fulfiller_same_as_offerer() {
let fulfiller = offerer;
Erc721Dispatcher { contract_address: nft_address }.mint(fulfiller, 'base_uri');
- snf::start_prank(CheatTarget::One(erc20_address), offerer);
+ cheat_caller_address(erc20_address, offerer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(nft_address), fulfiller);
+ cheat_caller_address(nft_address, fulfiller, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ("Offerer and fulfiller must be different",))]
+#[should_panic(expected: "Offerer and fulfiller must be different")]
fn test_fulfill_listing_order_fulfiller_same_as_offerer() {
let (executor_address, erc20_address, nft_address) = setup();
let start_amount = 10_000_000;
@@ -411,20 +383,17 @@ fn test_fulfill_listing_order_fulfiller_same_as_offerer() {
IFreeMintDispatcher { contract_address: erc20_address }.mint(fulfiller, start_amount);
- snf::start_prank(CheatTarget::One(nft_address), offerer);
+ cheat_caller_address(nft_address, offerer, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(erc20_address), fulfiller);
+ cheat_caller_address(erc20_address, fulfiller, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
@@ -437,7 +406,7 @@ fn test_fulfill_auction_order_ok() {
let (order_hash, offerer, token_id) = create_auction_order(
executor_address, erc20_address, nft_address, start_amount, end_amount
);
- let fulfiller = contract_address_const::<'fulfiller'>();
+ let fulfiller = offerer;
IFreeMintDispatcher { contract_address: erc20_address }.mint(buyer, start_amount);
@@ -446,25 +415,21 @@ fn test_fulfill_auction_order_ok() {
buyer_order.start_amount = start_amount;
buyer_order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor_address), buyer);
+ cheat_caller_address(executor_address, buyer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(buyer_order);
- snf::stop_prank(CheatTarget::One(executor_address));
- snf::start_prank(CheatTarget::One(nft_address), offerer);
+ cheat_caller_address(nft_address, offerer, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
let mut fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
fulfill_info.related_order_hash = Option::Some(buyer_order.compute_order_hash());
- snf::start_prank(CheatTarget::One(erc20_address), buyer);
+ cheat_caller_address(erc20_address, buyer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
@@ -486,29 +451,25 @@ fn test_fulfill_auction_order_fulfiller_same_as_offerer() {
buyer_order.start_amount = start_amount;
buyer_order.token_id = Option::Some(token_id);
- snf::start_prank(CheatTarget::One(executor_address), buyer);
+ cheat_caller_address(executor_address, buyer, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.create_order(buyer_order);
- snf::stop_prank(CheatTarget::One(executor_address));
- snf::start_prank(CheatTarget::One(nft_address), offerer);
+ cheat_caller_address(nft_address, offerer, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
let mut fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
fulfill_info.related_order_hash = Option::Some(buyer_order.compute_order_hash());
- snf::start_prank(CheatTarget::One(erc20_address), buyer);
+ cheat_caller_address(erc20_address, buyer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
#[test]
-#[should_panic(expected: ('Executor not enabled',))]
+#[should_panic(expected: 'Executor not enabled')]
fn test_fulfill_order_not_enabled() {
let (executor_address, erc20_address, nft_address) = setup();
let admin = contract_address_const::<'admin'>();
@@ -522,22 +483,18 @@ fn test_fulfill_order_not_enabled() {
executor_address, erc20_address, nft_address, token_id
);
- snf::start_prank(CheatTarget::One(erc20_address), offerer);
+ cheat_caller_address(erc20_address, offerer, CheatSpan::TargetCalls(1));
IERC20Dispatcher { contract_address: erc20_address }.approve(executor_address, start_amount);
- snf::stop_prank(CheatTarget::One(erc20_address));
let fulfill_info = create_fulfill_info(order_hash, fulfiller, nft_address, token_id);
- snf::start_prank(CheatTarget::One(nft_address), fulfiller);
+ cheat_caller_address(nft_address, fulfiller, CheatSpan::TargetCalls(1));
IERC721Dispatcher { contract_address: nft_address }
.set_approval_for_all(executor_address, true);
- snf::stop_prank(CheatTarget::One(nft_address));
- snf::start_prank(CheatTarget::One(executor_address), admin);
+ cheat_caller_address(executor_address, admin, CheatSpan::TargetCalls(1));
IMaintenanceDispatcher { contract_address: executor_address }.set_maintenance_mode(true);
- snf::stop_prank(CheatTarget::One(executor_address));
- snf::start_prank(CheatTarget::One(executor_address), fulfiller);
+ cheat_caller_address(executor_address, fulfiller, CheatSpan::TargetCalls(1));
IExecutorDispatcher { contract_address: executor_address }.fulfill_order(fulfill_info);
- snf::stop_prank(CheatTarget::One(executor_address));
}
diff --git a/contracts/ark_starknet/tests/integration/maintenance.cairo b/contracts/ark_starknet/tests/integration/maintenance.cairo
index 4f3e7160e..4f74e3ce8 100644
--- a/contracts/ark_starknet/tests/integration/maintenance.cairo
+++ b/contracts/ark_starknet/tests/integration/maintenance.cairo
@@ -1,10 +1,11 @@
-use starknet::{ContractAddress, contract_address_const};
-use ark_starknet::interfaces::{IMaintenanceDispatcher, IMaintenanceDispatcherTrait};
use ark_starknet::executor::executor;
+use ark_starknet::interfaces::{IMaintenanceDispatcher, IMaintenanceDispatcherTrait};
-use snforge_std as snf;
-use snf::cheatcodes::events::{EventFetcher, EventAssertions};
-use snf::{ContractClass, ContractClassTrait, CheatTarget, spy_events, SpyOn};
+use snforge_std::{
+ ContractClass, ContractClassTrait, cheat_caller_address, CheatSpan, spy_events,
+ EventSpyAssertionsTrait,
+};
+use starknet::{ContractAddress, contract_address_const};
use super::super::common::setup::deploy_executor;
@@ -13,8 +14,10 @@ fn admin_can_change_executor_state() {
let admin = contract_address_const::<'admin'>();
let executor_address = deploy_executor();
let executor = IMaintenanceDispatcher { contract_address: executor_address };
- let mut spy = spy_events(SpyOn::One(executor_address));
- snf::start_prank(snf::CheatTarget::One(executor_address), admin);
+
+ let mut spy = spy_events();
+
+ cheat_caller_address(executor_address, admin, CheatSpan::TargetCalls(1));
executor.set_maintenance_mode(true);
assert!(executor.is_in_maintenance(), "Executor should be in maintenance");
spy
@@ -28,7 +31,8 @@ fn admin_can_change_executor_state() {
)
]
);
-
+ let mut spy = spy_events();
+ cheat_caller_address(executor_address, admin, CheatSpan::TargetCalls(1));
executor.set_maintenance_mode(false);
assert!(!executor.is_in_maintenance(), "Executor should not be in maintenance");
spy
@@ -42,8 +46,6 @@ fn admin_can_change_executor_state() {
)
]
);
-
- snf::stop_prank(snf::CheatTarget::One(executor_address));
}
#[test]
@@ -52,9 +54,8 @@ fn only_admin_can_change_disable_executor() {
let executor_address = deploy_executor();
let alice = contract_address_const::<'alice'>();
- snf::start_prank(snf::CheatTarget::One(executor_address), alice);
+ cheat_caller_address(executor_address, alice, CheatSpan::TargetCalls(1));
IMaintenanceDispatcher { contract_address: executor_address }.set_maintenance_mode(true);
- snf::stop_prank(snf::CheatTarget::One(executor_address));
}
#[test]
@@ -63,7 +64,6 @@ fn only_admin_can_change_enable_executor() {
let executor_address = deploy_executor();
let alice = contract_address_const::<'alice'>();
- snf::start_prank(snf::CheatTarget::One(executor_address), alice);
+ cheat_caller_address(executor_address, alice, CheatSpan::TargetCalls(1));
IMaintenanceDispatcher { contract_address: executor_address }.set_maintenance_mode(false);
- snf::stop_prank(snf::CheatTarget::One(executor_address));
}
diff --git a/contracts/ark_starknet/tests/lib.cairo b/contracts/ark_starknet/tests/lib.cairo
index 9b47e3f91..604ac5ca7 100644
--- a/contracts/ark_starknet/tests/lib.cairo
+++ b/contracts/ark_starknet/tests/lib.cairo
@@ -7,7 +7,7 @@ mod unit {
}
mod integration {
mod create_order;
- mod execute_order;
+ // mod execute_order;
mod fees_amount;
mod fulfill_order;
mod maintenance;
diff --git a/contracts/ark_starknet/tests/unit/test_fees.cairo b/contracts/ark_starknet/tests/unit/test_fees.cairo
index 55ba5321b..dfa1e2ae1 100644
--- a/contracts/ark_starknet/tests/unit/test_fees.cairo
+++ b/contracts/ark_starknet/tests/unit/test_fees.cairo
@@ -1,10 +1,12 @@
use ark_starknet::executor::{executor};
use ark_starknet::interfaces::FeesRatio;
-use starknet::{ContractAddress};
+use snforge_std::{
+ start_cheat_caller_address_global, stop_cheat_caller_address_global, test_address
+};
use starknet::testing;
-use snforge_std as snf;
+use starknet::{ContractAddress};
#[test]
fn test_add_broker() {
@@ -12,15 +14,16 @@ fn test_add_broker() {
let fees_ratio = FeesRatio { numerator: 10, denominator: 100, };
- let broker_address = snf::test_address();
+ let broker_address = test_address();
// Call the add_broker method.
- snf::start_prank(snf::CheatTarget::All, broker_address);
+ start_cheat_caller_address_global(broker_address);
executor::ExecutorImpl::set_broker_fees(ref state, fees_ratio);
let result = executor::ExecutorImpl::get_broker_fees(@state, broker_address);
assert(result == fees_ratio, 'Fees are not equal');
+ stop_cheat_caller_address_global();
}
#[test]
@@ -43,9 +46,10 @@ fn test_fees_ratio_bigger_than_1_broker_fees() {
let fees_ratio = FeesRatio { numerator: 500, denominator: 100, };
- let broker_address = snf::test_address();
- snf::start_prank(snf::CheatTarget::All, broker_address);
+ let broker_address = test_address();
+ start_cheat_caller_address_global(broker_address);
executor::ExecutorImpl::set_broker_fees(ref state, fees_ratio);
+ stop_cheat_caller_address_global();
}
#[test]
@@ -65,9 +69,10 @@ fn test_fees_denominator_0_broker_fees() {
let fees_ratio = FeesRatio { numerator: 10, denominator: 0, };
- let broker_address = snf::test_address();
- snf::start_prank(snf::CheatTarget::All, broker_address);
+ let broker_address = test_address();
+ start_cheat_caller_address_global(broker_address);
executor::ExecutorImpl::set_broker_fees(ref state, fees_ratio);
+ stop_cheat_caller_address_global();
}
#[test]
diff --git a/contracts/ark_tokens/Scarb.toml b/contracts/ark_tokens/Scarb.toml
index 862324da4..cc8305bee 100644
--- a/contracts/ark_tokens/Scarb.toml
+++ b/contracts/ark_tokens/Scarb.toml
@@ -4,11 +4,16 @@ description = "StarkNet contracts for test tokens"
version = "0.1.0"
[dependencies]
-starknet = "2.5.4"
-snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.18.0" }
-openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" }
+starknet.workspace = true
+openzeppelin.workspace = true
ark_oz = { path = "../ark_oz" }
+[dev-dependencies]
+snforge_std.workspace = true
+
+[tool]
+fmt.workspace = true
+
[lib]
[[target.starknet-contract]]
diff --git a/contracts/ark_tokens/src/erc20.cairo b/contracts/ark_tokens/src/erc20.cairo
index 974f2cc5b..b8a04fbc8 100644
--- a/contracts/ark_tokens/src/erc20.cairo
+++ b/contracts/ark_tokens/src/erc20.cairo
@@ -8,6 +8,7 @@ trait IFreeMint {
#[starknet::contract]
mod FreeMintERC20 {
use openzeppelin::token::erc20::ERC20Component;
+ use openzeppelin::token::erc20::ERC20HooksEmptyImpl;
use starknet::ContractAddress;
use super::IFreeMint;
@@ -47,7 +48,7 @@ mod FreeMintERC20 {
#[abi(embed_v0)]
impl ImplFreeMint of IFreeMint {
fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {
- self.erc20._mint(recipient, amount);
+ self.erc20.mint(recipient, amount);
}
}
}
diff --git a/contracts/ark_tokens/src/erc721.cairo b/contracts/ark_tokens/src/erc721.cairo
index 930807baf..53e860d9e 100644
--- a/contracts/ark_tokens/src/erc721.cairo
+++ b/contracts/ark_tokens/src/erc721.cairo
@@ -8,14 +8,15 @@ trait IFreeMint {
#[starknet::contract]
mod FreeMintNFT {
+ use core::array::ArrayTrait;
+ use core::serde::Serde;
+ use core::traits::Into;
+ use core::traits::TryInto;
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc721::ERC721Component;
+ use openzeppelin::token::erc721::ERC721HooksEmptyImpl;
use starknet::ContractAddress;
- use core::traits::Into;
- use core::serde::Serde;
- use core::traits::TryInto;
use super::IFreeMint;
- use core::array::ArrayTrait;
component!(path: ERC721Component, storage: erc721, event: ERC721Event);
component!(path: SRC5Component, storage: src5, event: SRC5Event);
@@ -66,7 +67,7 @@ mod FreeMintNFT {
fn mint(ref self: ContractState, recipient: ContractAddress, token_uri: felt252) {
let token_id = self.latest_token_id.read();
- self.erc721._mint(recipient, token_id);
+ self.erc721.mint(recipient, token_id);
self.latest_token_id.write(token_id + 1);
}
}
diff --git a/contracts/ark_tokens/src/erc721_royalty.cairo b/contracts/ark_tokens/src/erc721_royalty.cairo
index 8aa3d1c8f..15e258801 100644
--- a/contracts/ark_tokens/src/erc721_royalty.cairo
+++ b/contracts/ark_tokens/src/erc721_royalty.cairo
@@ -8,18 +8,18 @@ trait IFreeMint {
#[starknet::contract]
mod FreeMintNFTRoyalty {
- use starknet::ContractAddress;
- use core::traits::Into;
+ use ark_oz::erc2981::ERC2981Component;
+ use ark_oz::erc2981::FeesRatioDefault;
+ use core::array::ArrayTrait;
use core::serde::Serde;
+ use core::traits::Into;
use core::traits::TryInto;
- use core::array::ArrayTrait;
+ use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc721::ERC721Component;
- use openzeppelin::access::ownable::OwnableComponent;
-
- use ark_oz::erc2981::ERC2981Component;
- use ark_oz::erc2981::FeesRatioDefault;
+ use openzeppelin::token::erc721::ERC721HooksEmptyImpl;
+ use starknet::ContractAddress;
use super::IFreeMint;
@@ -93,7 +93,7 @@ mod FreeMintNFTRoyalty {
fn mint(ref self: ContractState, recipient: ContractAddress, token_uri: felt252) {
let token_id = self.latest_token_id.read();
- self.erc721._mint(recipient, token_id);
+ self.erc721.mint(recipient, token_id);
self.latest_token_id.write(token_id + 1);
}
}
diff --git a/contracts/ark_tokens/src/lib.cairo b/contracts/ark_tokens/src/lib.cairo
index 5e9a35100..1af6a81a3 100644
--- a/contracts/ark_tokens/src/lib.cairo
+++ b/contracts/ark_tokens/src/lib.cairo
@@ -1,4 +1,4 @@
+mod erc20;
mod erc721;
mod erc721_royalty;
-mod erc20;
diff --git a/contracts/solis/Scarb.toml b/contracts/solis/Scarb.toml
index a9563e13b..b9603293c 100644
--- a/contracts/solis/Scarb.toml
+++ b/contracts/solis/Scarb.toml
@@ -7,6 +7,9 @@ version = "0.1.0"
[dependencies]
starknet = "2.5.4"
+[tool]
+fmt.workspace = true
+
[[target.starknet-contract]]
sierra = true
casm = true