From d81853a7ceeb37f99b14a3eedc3c28d1037877f0 Mon Sep 17 00:00:00 2001 From: De Fuse Date: Thu, 13 Jun 2024 14:37:38 +0400 Subject: [PATCH 1/8] refactor: separate crates for contract interfaces and impls --- Cargo.lock | 49 +++++ Cargo.toml | 34 ++- Makefile.toml | 31 +-- account-impl/Cargo.toml | 17 ++ {account => account-impl}/README.md | 0 {account => account-impl}/src/error.rs | 0 account-impl/src/lib.rs | 98 +++++++++ account-impl/src/types/account.rs | 1 + .../src/types/account_db.rs | 5 +- {account => account-impl}/src/types/mod.rs | 1 - account/Cargo.toml | 9 - account/src/{types => }/account.rs | 3 +- account/src/lib.rs | 116 ++-------- contracts/Cargo.toml | 9 + contracts/src/lib.rs | 3 + controller-impl/Cargo.toml | 12 ++ controller-impl/src/lib.rs | 21 ++ controller/Cargo.toml | 3 - controller/src/lib.rs | 19 +- intent-impl/Cargo.toml | 15 ++ {intent => intent-impl}/README.md | 0 intent-impl/src/lib.rs | 184 ++++++++++++++++ intent/Cargo.toml | 9 +- intent/src/error.rs | 27 +-- intent/src/intent.rs | 78 +++++++ intent/src/lib.rs | 198 ++---------------- intent/src/types/intent/mod.rs | 130 ------------ intent/src/types/mod.rs | 3 - res/fungible-token.wasm | Bin tests/src/tests/intent/env.rs | 2 +- tests/src/tests/intent/mod.rs | 14 +- tests/src/utils/intent.rs | 22 +- 32 files changed, 597 insertions(+), 516 deletions(-) create mode 100644 account-impl/Cargo.toml rename {account => account-impl}/README.md (100%) rename {account => account-impl}/src/error.rs (100%) create mode 100644 account-impl/src/lib.rs create mode 100644 account-impl/src/types/account.rs rename {account => account-impl}/src/types/account_db.rs (96%) rename {account => account-impl}/src/types/mod.rs (70%) rename account/src/{types => }/account.rs (57%) create mode 100644 contracts/Cargo.toml create mode 100644 contracts/src/lib.rs create mode 100644 controller-impl/Cargo.toml create mode 100644 controller-impl/src/lib.rs create mode 100644 intent-impl/Cargo.toml rename {intent => intent-impl}/README.md (100%) create mode 100644 intent-impl/src/lib.rs create mode 100644 intent/src/intent.rs delete mode 100644 intent/src/types/intent/mod.rs delete mode 100644 intent/src/types/mod.rs mode change 100755 => 100644 res/fungible-token.wasm diff --git a/Cargo.lock b/Cargo.lock index 66f22bd..e4989bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -884,6 +884,14 @@ dependencies = [ "near-sdk", ] +[[package]] +name = "defuse-account-contract-impl" +version = "0.1.0" +dependencies = [ + "defuse-account-contract", + "near-sdk", +] + [[package]] name = "defuse-contract-tests" version = "0.1.0" @@ -896,6 +904,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "defuse-contracts" +version = "0.1.0" +dependencies = [ + "defuse-account-contract", + "defuse-controller-contract", + "defuse-intent-contract", +] + [[package]] name = "defuse-controller-contract" version = "0.1.0" @@ -903,10 +920,30 @@ dependencies = [ "near-sdk", ] +[[package]] +name = "defuse-controller-contract-impl" +version = "0.1.0" +dependencies = [ + "defuse-account-contract", + "defuse-controller-contract", + "near-sdk", +] + [[package]] name = "defuse-intent-contract" version = "0.1.0" dependencies = [ + "near-contract-standards", + "near-sdk", + "strum 0.26.2", +] + +[[package]] +name = "defuse-intent-contract-impl" +version = "0.1.0" +dependencies = [ + "defuse-intent-contract", + "near-contract-standards", "near-sdk", ] @@ -1903,6 +1940,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "near-contract-standards" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e78fe14d389ae76f792735a2108a6b8332a2bed61197310bf5ad718fb1e424" +dependencies = [ + "near-sdk", +] + [[package]] name = "near-crypto" version = "0.20.1" @@ -3480,6 +3526,9 @@ name = "strum" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros 0.26.2", +] [[package]] name = "strum_macros" diff --git a/Cargo.toml b/Cargo.toml index 6a7f681..73c8541 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,37 @@ +[workspace] +resolver = "2" +members = [ + "account", + "account-impl", + "contracts", + "controller", + "controller-impl", + "intent", + "intent-impl", + "tests", +] + [workspace.package] edition = "2021" [workspace.dependencies] -anyhow = "1" +defuse-account-contract.path = "./account" +defuse-account-contract-impl.path = "./account-impl" +defuse-contracts.path = "./contracts" +defuse-controller-contract.path = "./controller" +defuse-controller-contract-impl.path = "./controller-impl" +defuse-intent-contract.path = "./intent" +defuse-intent-contract-impl.path = "./intent-impl" + near-sdk = "5.1" near-workspaces = "0.10" +near-contract-standards = "5.1.0" + +anyhow = "1" serde_json = "1" +strum = { version = "0.26", features = ["derive"] } tokio = { version = "1.38", default-features = false } -[workspace] -resolver = "2" -members = [ - "account", - "controller", - "intent", - "tests" -] [workspace.lints.clippy] all = "deny" diff --git a/Makefile.toml b/Makefile.toml index 1ead7fa..3e8bc51 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -22,27 +22,16 @@ dependencies = [ [tasks.clippy] command = "cargo" -dependencies = [ - "build", -] -args = [ - "clippy", - "--workspace", - "--all-targets" -] +dependencies = ["build"] +args = ["clippy", "--workspace", "--all-targets"] [tasks.test] alias = "tests" [tasks.tests] -dependencies = [ - "build" -] +dependencies = ["build"] command = "cargo" -args = [ - "test", - "--all-targets" -] +args = ["test", "--all-targets"] [tasks.build-account] command = "cargo" @@ -52,7 +41,7 @@ args = [ "${TARGET}", "--release", "--package", - "defuse-account-contract" + "defuse-account-contract-impl", ] [tasks.build-intent] @@ -63,7 +52,7 @@ args = [ "${TARGET}", "--release", "--package", - "defuse-intent-contract", + "defuse-intent-contract-impl", ] [tasks.build-controller] @@ -74,18 +63,18 @@ args = [ "${TARGET}", "--release", "-p", - "defuse-controller-contract" + "defuse-controller-contract-impl", ] [tasks.cp-contracts] script = """ -cp target/${TARGET}/release/defuse_account_contract.wasm ${TARGET_DIR}/${ACCOUNT_WASM_FILE} -cp target/${TARGET}/release/defuse_intent_contract.wasm ${TARGET_DIR}/${INTENT_WASM_FILE} +cp target/${TARGET}/release/defuse_account_contract_impl.wasm ${TARGET_DIR}/${ACCOUNT_WASM_FILE} +cp target/${TARGET}/release/defuse_intent_contract_impl.wasm ${TARGET_DIR}/${INTENT_WASM_FILE} """ [tasks.cp-controller] script = """ -cp target/${TARGET}/release/defuse_controller_contract.wasm ${TARGET_DIR}/${CONTROLLER_WASM_FILE} +cp target/${TARGET}/release/defuse_controller_contract_impl.wasm ${TARGET_DIR}/${CONTROLLER_WASM_FILE} """ [tasks.clean] diff --git a/account-impl/Cargo.toml b/account-impl/Cargo.toml new file mode 100644 index 0000000..404e0bc --- /dev/null +++ b/account-impl/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "defuse-account-contract-impl" +version = "0.1.0" +edition.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[lints] +workspace = true + +[dependencies] +defuse-account-contract.workspace = true +near-sdk.workspace = true + +[dev-dependencies] +near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/account/README.md b/account-impl/README.md similarity index 100% rename from account/README.md rename to account-impl/README.md diff --git a/account/src/error.rs b/account-impl/src/error.rs similarity index 100% rename from account/src/error.rs rename to account-impl/src/error.rs diff --git a/account-impl/src/lib.rs b/account-impl/src/lib.rs new file mode 100644 index 0000000..ce87fad --- /dev/null +++ b/account-impl/src/lib.rs @@ -0,0 +1,98 @@ +use defuse_account_contract::{Account, AccountContract}; +use near_sdk::store::LookupSet; +use near_sdk::{ + env, ext_contract, near, AccountId, BorshStorageKey, PanicOnDefault, PromiseOrValue, +}; + +use crate::error::LogError; +use crate::types::AccountDb; + +mod error; +mod types; + +#[derive(BorshStorageKey)] +#[near(serializers=[borsh])] +enum Prefix { + Accounts, + Indexers, +} + +#[near(contract_state)] +#[derive(PanicOnDefault)] +pub struct AccountContractImpl { + owner_id: AccountId, + /// MPC contract id. + mpc_contract_id: AccountId, + /// List of indexers. Accounts which allow to add a new account. + indexers: LookupSet, + /// Key here is `account_id + '.' + derivation_path` + /// Value is account id of the owner. + accounts: AccountDb, +} + +#[near] +impl AccountContract for AccountContractImpl { + fn create_account(&mut self, account_id: AccountId, derivation_path: String) { + // Only indexers can call this transaction. + let predecessor_id = env::predecessor_account_id(); + self.assert_indexer(&predecessor_id); + + self.accounts + .add_account(account_id, derivation_path, Account::default()) + .log_error(); + } + + fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String) { + self.accounts + .change_owner(from, to, derivation_path) + .log_error(); + } + + fn get_accounts(&self, account_id: &AccountId) -> Vec<(String, Account)> { + self.accounts.get_accounts(account_id).log_error() + } + + fn mpc_contract(&self) -> &AccountId { + &self.mpc_contract_id + } +} + +#[near] +impl AccountContractImpl { + #[init] + #[must_use] + #[allow(clippy::use_self)] + pub fn new(owner_id: AccountId, mpc_contract_id: AccountId) -> Self { + Self { + owner_id, + mpc_contract_id, + indexers: LookupSet::new(Prefix::Indexers), + accounts: AccountDb::new(Prefix::Accounts), + } + } + + #[private] + pub fn set_mpc_contract(&mut self, contract_id: AccountId) { + self.mpc_contract_id = contract_id; + } + + fn assert_indexer(&self, account_id: &AccountId) { + assert!( + self.indexers.contains(account_id), + "Only indexers allow adding an account" + ); + } +} + +#[ext_contract(ext_mpc)] +pub trait MpcRecovery { + fn sign( + &self, + payload: Vec, + path: &str, + key_version: u32, + ) -> PromiseOrValue<(String, String)>; +} + +#[cfg(test)] +mod contract_tests {} diff --git a/account-impl/src/types/account.rs b/account-impl/src/types/account.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/account-impl/src/types/account.rs @@ -0,0 +1 @@ + diff --git a/account/src/types/account_db.rs b/account-impl/src/types/account_db.rs similarity index 96% rename from account/src/types/account_db.rs rename to account-impl/src/types/account_db.rs index ce01e21..e2a8a7a 100644 --- a/account/src/types/account_db.rs +++ b/account-impl/src/types/account_db.rs @@ -1,9 +1,8 @@ -use near_sdk::store::LookupMap; -use near_sdk::{near, AccountId, IntoStorageKey}; +use defuse_account_contract::Account; +use near_sdk::{near, store::LookupMap, AccountId, IntoStorageKey}; use std::collections::HashMap; use crate::error::Error; -use crate::types::Account; // Accounts that belong user. Key here is derivation path. type UserAccounts = HashMap; diff --git a/account/src/types/mod.rs b/account-impl/src/types/mod.rs similarity index 70% rename from account/src/types/mod.rs rename to account-impl/src/types/mod.rs index 5768955..8e9fede 100644 --- a/account/src/types/mod.rs +++ b/account-impl/src/types/mod.rs @@ -1,4 +1,3 @@ -pub use account::Account; pub use account_db::AccountDb; mod account; diff --git a/account/Cargo.toml b/account/Cargo.toml index 8c91bf7..828d0e2 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -3,14 +3,5 @@ name = "defuse-account-contract" version = "0.1.0" edition.workspace = true -[lib] -crate-type = ["cdylib", "rlib"] - -[lints] -workspace = true - [dependencies] near-sdk.workspace = true - -[dev-dependencies] -near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/account/src/types/account.rs b/account/src/account.rs similarity index 57% rename from account/src/types/account.rs rename to account/src/account.rs index 75c3cb1..a930f28 100644 --- a/account/src/types/account.rs +++ b/account/src/account.rs @@ -1,7 +1,6 @@ use near_sdk::near; -#[derive(Default, Clone)] -#[cfg_attr(test, derive(Debug, Eq, PartialEq))] +#[derive(Debug, Clone, Default, Eq, PartialEq)] #[near(serializers=[borsh, json])] pub struct Account { is_locked: bool, diff --git a/account/src/lib.rs b/account/src/lib.rs index 2698aa2..0a8e018 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -1,97 +1,21 @@ -use near_sdk::store::LookupSet; -use near_sdk::{ - env, ext_contract, near, AccountId, BorshStorageKey, PanicOnDefault, PromiseOrValue, -}; - -use crate::error::LogError; -use crate::types::{Account, AccountDb}; - -mod error; -mod types; - -#[derive(BorshStorageKey)] -#[near(serializers=[borsh])] -enum Prefix { - Accounts, - Indexers, -} - -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct AccountContract { - owner_id: AccountId, - /// MPC contract id. - mpc_contract_id: AccountId, - /// List of indexers. Accounts which allow to add a new account. - indexers: LookupSet, - /// Key here is `account_id + '.' + derivation_path` - /// Value is account id of the owner. - accounts: AccountDb, +mod account; + +pub use self::account::*; + +use near_sdk::{ext_contract, AccountId}; + +// TODO: make this contract an extension of NFT, since every Account +// is a unique item. This will ease the integration of Defuse +// with already existing tooling around NFTs +#[ext_contract(ext_account_contract)] +pub trait AccountContract { + /// Create an account with given defivation path for given owner + fn create_account(&mut self, owner: AccountId, derivation_path: String); + /// Change an owner of account with given derivation path. + fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String); + /// Return all [`Account`]s owned by given `owner` + fn get_accounts(&self, owner: &AccountId) -> Vec<(String, Account)>; + + /// Return MPC contract for this + fn mpc_contract(&self) -> &AccountId; } - -#[near] -impl AccountContract { - #[init] - #[must_use] - #[allow(clippy::use_self)] - pub fn new(owner_id: AccountId, mpc_contract_id: AccountId) -> Self { - Self { - owner_id, - mpc_contract_id, - indexers: LookupSet::new(Prefix::Indexers), - accounts: AccountDb::new(Prefix::Accounts), - } - } - - /// Add a new ownership of a new tokens. - pub fn add_account(&mut self, account_id: AccountId, derivation_path: String) { - // Only indexers can call this transaction. - let predecessor_id = env::predecessor_account_id(); - self.assert_indexer(&predecessor_id); - - self.accounts - .add_account(account_id, derivation_path, Account::default()) - .log_error(); - } - - /// Change an owner of the account. - pub fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String) { - self.accounts - .change_owner(from, to, derivation_path) - .log_error(); - } - - /// Return a user's accounts. - pub fn get_accounts(&self, account_id: &AccountId) -> Vec<(String, Account)> { - self.accounts.get_accounts(account_id).log_error() - } - - #[private] - pub fn set_mpc_contract(&mut self, contract_id: AccountId) { - self.mpc_contract_id = contract_id; - } - - pub const fn get_mpc_contract(&self) -> &AccountId { - &self.mpc_contract_id - } - - fn assert_indexer(&self, account_id: &AccountId) { - assert!( - self.indexers.contains(account_id), - "Only indexers allow adding an account" - ); - } -} - -#[ext_contract(ext_mpc)] -pub trait MpcRecovery { - fn sign( - &self, - payload: Vec, - path: &str, - key_version: u32, - ) -> PromiseOrValue<(String, String)>; -} - -#[cfg(test)] -mod contract_tests {} diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml new file mode 100644 index 0000000..e66d964 --- /dev/null +++ b/contracts/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "defuse-contracts" +version = "0.1.0" +edition.workspace = true + +[dependencies] +defuse-account-contract.workspace = true +defuse-controller-contract.workspace = true +defuse-intent-contract.workspace = true diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs new file mode 100644 index 0000000..968a1e6 --- /dev/null +++ b/contracts/src/lib.rs @@ -0,0 +1,3 @@ +pub use defuse_account_contract as account; +pub use defuse_controller_contract as controller; +pub use defuse_intent_contract as intent; diff --git a/controller-impl/Cargo.toml b/controller-impl/Cargo.toml new file mode 100644 index 0000000..b22454e --- /dev/null +++ b/controller-impl/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "defuse-controller-contract-impl" +version = "0.1.0" +edition.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +defuse-account-contract.workspace = true +defuse-controller-contract.workspace = true +near-sdk.workspace = true diff --git a/controller-impl/src/lib.rs b/controller-impl/src/lib.rs new file mode 100644 index 0000000..bbff652 --- /dev/null +++ b/controller-impl/src/lib.rs @@ -0,0 +1,21 @@ +use defuse_controller_contract::ControllerContract; +use near_sdk::{near, AccountId, PanicOnDefault}; + +#[near(contract_state)] +#[derive(PanicOnDefault)] +pub struct ControllerContractImpl { + owner_id: AccountId, +} + +#[near] +impl ControllerContract for ControllerContractImpl {} + +#[near] +impl ControllerContractImpl { + #[init] + #[must_use] + #[allow(clippy::use_self)] + pub const fn new(owner_id: AccountId) -> Self { + Self { owner_id } + } +} diff --git a/controller/Cargo.toml b/controller/Cargo.toml index dc8bd1b..cc041b8 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -3,9 +3,6 @@ name = "defuse-controller-contract" version = "0.1.0" edition.workspace = true -[lib] -crate-type = ["cdylib", "rlib"] - [lints] workspace = true diff --git a/controller/src/lib.rs b/controller/src/lib.rs index f8c734f..7957286 100644 --- a/controller/src/lib.rs +++ b/controller/src/lib.rs @@ -1,17 +1,4 @@ -use near_sdk::{near, AccountId, PanicOnDefault}; +use near_sdk::ext_contract; -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct ControllerContract { - owner_id: AccountId, -} - -#[near] -impl ControllerContract { - #[init] - #[must_use] - #[allow(clippy::use_self)] - pub const fn new(owner_id: AccountId) -> Self { - Self { owner_id } - } -} +#[ext_contract(ext_controller_contract)] +pub trait ControllerContract {} diff --git a/intent-impl/Cargo.toml b/intent-impl/Cargo.toml new file mode 100644 index 0000000..b40b296 --- /dev/null +++ b/intent-impl/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "defuse-intent-contract-impl" +version = "0.1.0" +edition.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[lints] +workspace = true + +[dependencies] +defuse-intent-contract.workspace = true +near-sdk.workspace = true +near-contract-standards.workspace = true diff --git a/intent/README.md b/intent-impl/README.md similarity index 100% rename from intent/README.md rename to intent-impl/README.md diff --git a/intent-impl/src/lib.rs b/intent-impl/src/lib.rs new file mode 100644 index 0000000..c9a2329 --- /dev/null +++ b/intent-impl/src/lib.rs @@ -0,0 +1,184 @@ +use defuse_intent_contract::{Action, Intent, IntentContract}; +use near_contract_standards::fungible_token::core::ext_ft_core; +use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; +use near_sdk::{ + env::{self, panic_str}, + json_types::U128, + log, near, + store::{LookupMap, LookupSet}, + AccountId, BorshStorageKey, NearToken, PanicOnDefault, Promise, PromiseOrValue, +}; + +#[derive(BorshStorageKey)] +#[near(serializers=[borsh])] +enum Prefix { + SupportedTokens, + AllowedSolvers, + Intents, +} + +#[near(contract_state)] +#[derive(PanicOnDefault)] +pub struct IntentContractImpl { + owner_id: AccountId, + supported_tokens: LookupSet, + allowed_solvers: LookupSet, + intents: LookupMap, +} + +#[near] +impl IntentContract for IntentContractImpl { + fn add_solver(&mut self, solver_id: AccountId) { + self.assert_owner(); + self.allowed_solvers.insert(solver_id); + } + + fn rollback_intent(&mut self, id: String) -> Promise { + let intent = self + .intents + .get(&id) + .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); + let predecessor_id = env::predecessor_account_id(); + + assert!( + predecessor_id == intent.initiator + || predecessor_id == self.owner_id + || predecessor_id == env::current_account_id(), + "Only initiator, self or owner can roll back the intent" + ); + + ext_ft_core::ext(intent.send.token_id.clone()) + .with_attached_deposit(NearToken::from_yoctonear(1)) + .ft_transfer(intent.initiator.clone(), intent.send.amount, None) + .then(Self::ext(env::current_account_id()).cleanup_intent(&id)) + } + + fn get_intent(&self, id: String) -> Option<&Intent> { + self.intents.get(&id) + } + + fn is_allowed_solver(&self, solver_id: AccountId) -> bool { + self.allowed_solvers.contains(&solver_id) + } +} + +#[near] +impl FungibleTokenReceiver for IntentContractImpl { + /// The callback is called by NEP-141 after `ft_transfer_call`. + /// + /// # Panics + /// + /// The panic occurs if an attempt to add an intent with an existing id or execute + /// a nonexistent intent. + fn ft_on_transfer( + &mut self, + sender_id: AccountId, + amount: U128, + msg: String, + ) -> PromiseOrValue { + // Validate that sender_id is in white token list. + // self.assert_token(&sender_id); // TODO: Check if we need tokens validation. + + let action = Action::base64_decode(&msg).expect("decode Action"); + + log!(format!("{sender_id} : {}: msg: {msg}", amount.0)); + + match action { + Action::CreateIntent(id, intent) => { + assert!( + self.intents.insert(id, intent).is_none(), + "Intent already exists" + ); + + PromiseOrValue::Value(0.into()) + } + Action::ExecuteIntent(id) => { + let current_id = env::current_account_id(); + let solver_id = env::signer_account_id(); + self.assert_solver(&solver_id); + + let intent = self + .intents + .get(&id) + .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); + + let promise = if intent.is_expired() { + Self::ext(current_id).rollback_intent(id) + } else { + ext_ft_core::ext(intent.send.token_id.clone()) + .with_attached_deposit(NearToken::from_yoctonear(1)) + .ft_transfer(solver_id, intent.send.amount, None) + .then( + ext_ft_core::ext(intent.receive.token_id.clone()) + .with_attached_deposit(NearToken::from_yoctonear(1)) + .ft_transfer(intent.initiator.clone(), intent.receive.amount, None), + ) + .then(Self::ext(current_id).cleanup_intent(&id)) + }; + + PromiseOrValue::Promise(promise) + } + } + } +} + +#[near] +impl IntentContractImpl { + /// Contract constructor. + #[init] + #[must_use] + #[allow(clippy::use_self)] + pub fn new(owner_id: AccountId) -> Self { + Self { + owner_id, + supported_tokens: LookupSet::new(Prefix::SupportedTokens), + allowed_solvers: LookupSet::new(Prefix::AllowedSolvers), + intents: LookupMap::new(Prefix::Intents), + } + } + + /// Callback which removes an intent after successful execution. + #[private] + pub fn cleanup_intent(&mut self, intent_id: &String) { + self.intents.remove(intent_id); + } + + /// Set a new owner of the contract. + #[inline] + pub fn set_owner(&mut self, owner_id: AccountId) { + self.assert_owner(); + self.owner_id = owner_id; + } + + /// Return owner of the contract. + #[inline] + pub const fn get_owner(&self) -> &AccountId { + &self.owner_id + } + + #[inline] + fn assert_owner(&self) { + assert_eq!( + self.owner_id, + env::predecessor_account_id(), + "Only owner is allowed to add a new solver" + ); + } + + #[inline] + fn assert_solver(&self, solver_id: &AccountId) { + assert!( + self.allowed_solvers.contains(solver_id), + "The solver is not allowed" + ); + } + + #[allow(dead_code)] + #[inline] + fn assert_token(&self, token_id: &AccountId) { + assert!( + self.supported_tokens.contains(token_id.as_str()), + "Unsupported token" + ); + } +} diff --git a/intent/Cargo.toml b/intent/Cargo.toml index 37d88bc..c0eef77 100644 --- a/intent/Cargo.toml +++ b/intent/Cargo.toml @@ -3,11 +3,8 @@ name = "defuse-intent-contract" version = "0.1.0" edition.workspace = true -[lib] -crate-type = ["cdylib", "rlib"] - -[lints] -workspace = true - [dependencies] near-sdk.workspace = true +near-contract-standards.workspace = true + +strum.workspace = true \ No newline at end of file diff --git a/intent/src/error.rs b/intent/src/error.rs index 3b1f6c3..e98744b 100644 --- a/intent/src/error.rs +++ b/intent/src/error.rs @@ -1,19 +1,20 @@ +use near_sdk::base64; +use strum::IntoStaticStr; + #[allow(clippy::module_name_repetitions)] -#[derive(Debug)] +#[derive(Debug, IntoStaticStr)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] pub enum ContractError { - BorshSerializeError, - BorshDeserializeError, - Base64EncodeError, - Base64DecodeError, + #[strum(serialize = "BORSH_SERIALIZE_ERROR")] + BorshSerialize, + #[strum(serialize = "BORSH_DESERIALIZE_ERROR")] + BorshDeserialize, + #[strum(serialize = "BASE64_DECODE_ERROR")] + Base64Decode, } -impl AsRef for ContractError { - fn as_ref(&self) -> &str { - match self { - Self::BorshSerializeError => "BORSH_SERIALIZE_ERROR", - Self::BorshDeserializeError => "BORSH_DESERIALIZE_ERROR", - Self::Base64EncodeError => "BASE64_ENCODE_ERROR", - Self::Base64DecodeError => "BASE64_DECODE_ERROR", - } +impl From for ContractError { + fn from(_: base64::DecodeError) -> Self { + Self::Base64Decode } } diff --git a/intent/src/intent.rs b/intent/src/intent.rs new file mode 100644 index 0000000..2de12ef --- /dev/null +++ b/intent/src/intent.rs @@ -0,0 +1,78 @@ +use near_sdk::{ + base64::engine::{general_purpose::STANDARD, Engine}, + borsh::{self, BorshDeserialize}, + env, + json_types::U128, + near, AccountId, +}; + +use crate::ContractError; + +#[near(serializers=[borsh])] +pub enum Action { + CreateIntent( + /// ID + String, + Intent, + ), + ExecuteIntent( + /// ID + String, + ), +} + +impl Action { + pub fn base64_decode(msg: impl AsRef<[u8]>) -> Result { + Self::try_from_slice(&STANDARD.decode(msg)?).map_err(|_| ContractError::BorshDeserialize) + } + + pub fn encode_base64(&self) -> Result { + Ok(STANDARD.encode(borsh::to_vec(self).map_err(|_| ContractError::BorshSerialize)?)) + } +} + +/// Intent for swapping NEP-141 tokens +#[near(serializers=[borsh, json])] +pub struct Intent { + /// Initiator of the intent + pub initiator: AccountId, + /// Tokens the initiator wants to exchange + pub send: TokenAmount, + /// Tokens the initiator wants to get instead + pub receive: TokenAmount, + /// Intent expiration + pub expiration: Expiration, + /// Referral for getting a fee + pub referral: Option, +} + +impl Intent { + #[must_use] + pub fn is_expired(&self) -> bool { + match self.expiration { + Expiration::None => false, + Expiration::Time(time) => time * 1000 <= env::block_timestamp_ms(), + Expiration::Block(block) => block <= env::block_height(), + } + } +} + +/// Intent expiration +#[derive(Default, Debug)] +#[near(serializers=[borsh, json])] +pub enum Expiration { + /// No expiration + #[default] + None, + /// Expiration time in seconds. + Time(u64), + /// Expiration block. + Block(u64), +} + +#[derive(Debug, Clone)] +#[near(serializers=[borsh, json])] +pub struct TokenAmount { + pub token_id: AccountId, + pub amount: U128, +} diff --git a/intent/src/lib.rs b/intent/src/lib.rs index f1e5ee7..ab7e82e 100644 --- a/intent/src/lib.rs +++ b/intent/src/lib.rs @@ -1,116 +1,15 @@ -use near_sdk::env::panic_str; -use near_sdk::json_types::U128; -use near_sdk::store::{LookupMap, LookupSet}; -use near_sdk::{ - env, ext_contract, log, near, AccountId, BorshStorageKey, NearToken, PanicOnDefault, Promise, - PromiseOrValue, -}; +mod error; +mod intent; -use crate::{types::intent::Action, types::Intent}; +pub use self::{error::*, intent::*}; -pub mod error; -pub mod types; +use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; +use near_sdk::{ext_contract, AccountId, Promise}; -#[derive(BorshStorageKey)] -#[near(serializers=[borsh])] -enum Prefix { - SupportedTokens, - AllowedSolvers, - Intents, -} - -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct IntentContract { - owner_id: AccountId, - supported_tokens: LookupSet, - allowed_solvers: LookupSet, - intents: LookupMap, -} - -#[near] -impl IntentContract { - /// Contract constructor. - #[init] - #[must_use] - #[allow(clippy::use_self)] - pub fn new(owner_id: AccountId) -> Self { - Self { - owner_id, - supported_tokens: LookupSet::new(Prefix::SupportedTokens), - allowed_solvers: LookupSet::new(Prefix::AllowedSolvers), - intents: LookupMap::new(Prefix::Intents), - } - } - - /// Add a new solver to the whitelist. - pub fn add_solver(&mut self, solver_id: AccountId) { - self.assert_owner(); - self.allowed_solvers.insert(solver_id); - } - - /// The callback is called by NEP-141 after `ft_transfer_call`. - /// - /// # Panics - /// - /// The panic occurs if an attempt to add an intent with an existing id or execute - /// a nonexistent intent. - pub fn ft_on_transfer( - &mut self, - sender_id: &AccountId, - amount: U128, - msg: &String, - ) -> PromiseOrValue { - // Validate that sender_id is in white token list. - // self.assert_token(&sender_id); // TODO: Check if we need tokens validation. - let action = Action::decode(msg) - .unwrap_or_else(|e| panic_str(&format!("Action decode error: {}", e.as_ref()))); - - log!(format!("{sender_id} : {}: msg: {msg}", amount.0)); - - match action { - Action::CreateIntent((id, intent)) => { - assert!( - self.intents.insert(id, intent).is_none(), - "Intent already exists" - ); - - PromiseOrValue::Value(0.into()) - } - Action::ExecuteIntent(id) => { - let current_id = env::current_account_id(); - let solver_id = env::signer_account_id(); - self.assert_solver(&solver_id); - - let intent = self - .intents - .get(&id) - .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); - - let promise = if intent.is_expired() { - Self::ext(current_id).rollback_intent(&id) - } else { - ext_ft::ext(intent.send.token_id.clone()) - .with_attached_deposit(NearToken::from_yoctonear(1)) - .ft_transfer(solver_id, intent.send.amount) - .then( - ext_ft::ext(intent.receive.token_id.clone()) - .with_attached_deposit(NearToken::from_yoctonear(1)) - .ft_transfer(intent.initiator.clone(), intent.receive.amount), - ) - .then(Self::ext(current_id).cleanup_intent(&id)) - }; - - PromiseOrValue::Promise(promise) - } - } - } - - /// Callback which removes an intent after successful execution. - #[private] - pub fn cleanup_intent(&mut self, intent_id: &String) { - self.intents.remove(intent_id); - } +#[ext_contract(ext_intent_contract)] +pub trait IntentContract: FungibleTokenReceiver { + /// Return pending intent by id. + fn get_intent(&self, id: String) -> Option<&Intent>; /// Rollback created intent and refund tokens to the intent's initiator. /// The transaction could be called by an intent initiator or owner. @@ -118,82 +17,11 @@ impl IntentContract { /// # Panics /// /// The panic occurs if intent doesn't exist of caller is not allowed. - pub fn rollback_intent(&mut self, id: &String) -> Promise { - let intent = self - .intents - .get(id) - .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); - let predecessor_id = env::predecessor_account_id(); - - assert!( - predecessor_id == intent.initiator - || predecessor_id == self.owner_id - || predecessor_id == env::current_account_id(), - "Only initiator, self or owner can roll back the intent" - ); - - ext_ft::ext(intent.send.token_id.clone()) - .with_attached_deposit(NearToken::from_yoctonear(1)) - .ft_transfer(intent.initiator.clone(), intent.send.amount) - .then(Self::ext(env::current_account_id()).cleanup_intent(id)) - } + fn rollback_intent(&mut self, id: String) -> Promise; - /// Set a new owner of the contract. - pub fn set_owner(&mut self, owner_id: AccountId) { - self.assert_owner(); - self.owner_id = owner_id; - } - - /// Return owner of the contract. - pub const fn get_owner(&self) -> &AccountId { - &self.owner_id - } - - /// Return pending intent by id. - pub fn get_intent(&self, id: &String) -> Option<&Intent> { - self.intents.get(id) - } + /// Add a new solver to the whitelist. + fn add_solver(&mut self, solver_id: AccountId); /// Check if the provided solver is allowed. - pub fn is_allowed_solver(&self, solver_id: &AccountId) -> bool { - self.allowed_solvers.contains(solver_id) - } - - fn assert_owner(&self) { - assert_eq!( - self.owner_id, - env::predecessor_account_id(), - "Only owner is allowed to add a new solver" - ); - } - - fn assert_solver(&self, solver_id: &AccountId) { - assert!( - self.allowed_solvers.contains(solver_id), - "The solver is not allowed" - ); - } - - #[allow(dead_code)] - fn assert_token(&self, token_id: &AccountId) { - assert!( - self.supported_tokens.contains(token_id.as_str()), - "Unsupported token" - ); - } -} - -#[ext_contract(ext_ft)] -pub trait FungibleToken { - fn ft_balance_of(&self, account_id: AccountId) -> U128; - - fn ft_transfer(&self, receiver_id: AccountId, amount: U128); - - fn ft_transfer_call( - &mut self, - receiver_id: AccountId, - amount: U128, - memo: Option, - msg: String, - ) -> PromiseOrValue; + fn is_allowed_solver(&self, solver_id: AccountId) -> bool; } diff --git a/intent/src/types/intent/mod.rs b/intent/src/types/intent/mod.rs deleted file mode 100644 index 2b01f92..0000000 --- a/intent/src/types/intent/mod.rs +++ /dev/null @@ -1,130 +0,0 @@ -use near_sdk::json_types::U128; -use near_sdk::{ - base64::{engine::general_purpose::STANDARD, Engine}, - borsh::BorshDeserialize, - env, near, AccountId, -}; - -use crate::error::ContractError; - -/// Intent for swapping NEP-141 tokens -#[near(serializers=[borsh, json])] -pub struct Intent { - /// Initiator of the intent - pub initiator: AccountId, - /// Tokens the initiator wants to exchange - pub send: TokenAmount, - /// Tokens the initiator wants to get instead - pub receive: TokenAmount, - /// Intent expiration - pub expiration: Expiration, - /// Referral for getting a fee - pub referral: Option, -} - -impl Intent { - #[must_use] - pub fn is_expired(&self) -> bool { - match self.expiration { - Expiration::None => false, - Expiration::Time(time) => time * 1000 <= env::block_timestamp_ms(), - Expiration::Block(block) => block <= env::block_height(), - } - } -} - -#[near(serializers=[borsh])] -pub enum Action { - CreateIntent((String, Intent)), - ExecuteIntent(String), -} - -impl Action { - /// Decode provided msg into `Action`. - /// - /// # Errors - /// - /// `Base64DecodeError` - /// `BorshDeserializeError` - pub fn decode(msg: &str) -> Result { - let bytes = STANDARD - .decode(msg) - .map_err(|_| ContractError::Base64DecodeError)?; - - Self::try_from_slice(&bytes).map_err(|_| ContractError::BorshDeserializeError) - } - - /// Encode the action into a string. - /// - /// # Errors - /// - /// `BorshSerializeError` - pub fn encode(&self) -> Result { - near_sdk::borsh::to_vec(&self) - .map(|bytes| STANDARD.encode(bytes)) - .map_err(|_| ContractError::BorshSerializeError) - } -} - -/// Intent expiration -#[derive(Default, Debug)] -#[near(serializers=[borsh, json])] -pub enum Expiration { - /// No expiration - #[default] - None, - /// Expiration time in seconds. - Time(u64), - /// Expiration block. - Block(u64), -} - -#[derive(Debug, Clone)] -#[near(serializers=[borsh, json])] -pub struct TokenAmount { - pub token_id: AccountId, - pub amount: U128, -} - -#[test] -fn test_create_action_serialize() { - let action = Action::CreateIntent(( - "1".to_string(), - Intent { - initiator: "user.near".parse().unwrap(), - send: TokenAmount { - token_id: "token_a.near".parse().unwrap(), - amount: 1000.into(), - }, - receive: TokenAmount { - token_id: "token_b.near".parse().unwrap(), - amount: 2000.into(), - }, - expiration: Expiration::Block(123_456), - referral: Some("referral.near".parse().unwrap()), - }, - )); - - assert_eq!( - action.encode().unwrap(), - "AAEAAAAxCQAAAHVzZXIubmVhcgwAAAB0b2tlbl9hLm5lYXLoAwAAAAAAAAAAAAAAAAAADAAAAHRva2VuX2IubmVhctAHAAAAAAAAAAAAAAAAAAACQOIBAAAAAAABDQAAAHJlZmVycmFsLm5lYXI=" - ); -} - -#[test] -fn test_create_action_deserialize() { - let action = Action::decode("AAEAAAAxCQAAAHVzZXIubmVhcgwAAAB0b2tlbl9hLm5lYXLoAwAAAAAAAAAAAAAAAAAADAAAAHRva2VuX2IubmVhctAHAAAAAAAAAAAAAAAAAAACQOIBAAAAAAABDQAAAHJlZmVycmFsLm5lYXI=").unwrap(); - assert!(matches!(action, Action::CreateIntent((id, _)) if id == "1")); -} - -#[test] -fn test_execute_action_serialize() { - let action = Action::ExecuteIntent("1".to_string()); - assert_eq!(action.encode().unwrap(), "AQEAAAAx"); -} - -#[test] -fn test_execute_action_deserialize() { - let action = Action::decode("AQEAAAAx").unwrap(); - assert!(matches!(action, Action::ExecuteIntent(id) if id == "1")); -} diff --git a/intent/src/types/mod.rs b/intent/src/types/mod.rs deleted file mode 100644 index ca2201d..0000000 --- a/intent/src/types/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub use intent::Intent; - -pub mod intent; diff --git a/res/fungible-token.wasm b/res/fungible-token.wasm old mode 100755 new mode 100644 diff --git a/tests/src/tests/intent/env.rs b/tests/src/tests/intent/env.rs index 51a0129..63969a9 100644 --- a/tests/src/tests/intent/env.rs +++ b/tests/src/tests/intent/env.rs @@ -1,4 +1,4 @@ -use crate::utils::intent::Intent; +use crate::utils::intent::Intending; use crate::utils::token::Token; use crate::utils::Sandbox; use near_sdk::AccountId; diff --git a/tests/src/tests/intent/mod.rs b/tests/src/tests/intent/mod.rs index 6284a32..924950d 100644 --- a/tests/src/tests/intent/mod.rs +++ b/tests/src/tests/intent/mod.rs @@ -1,8 +1,8 @@ -use defuse_intent_contract::{types, types::intent::Expiration, types::intent::TokenAmount}; +use defuse_intent_contract::{Expiration, Intent, TokenAmount}; use crate::{ tests::intent::env::Env, - utils::{intent::Intent, token::Token}, + utils::{intent::Intending, token::Token}, }; mod env; @@ -28,7 +28,7 @@ async fn test_generic_successful_flow() { env.token_a.id(), env.intent.id(), "1", - types::Intent { + Intent { initiator: env.user_id().clone(), send: TokenAmount { token_id: env.token_a.id().clone(), @@ -86,7 +86,7 @@ async fn test_successful_flow_partly() { env.token_a.id(), env.intent.id(), "1", - types::Intent { + Intent { initiator: env.user_id().clone(), send: TokenAmount { token_id: env.token_a.id().clone(), @@ -164,7 +164,7 @@ async fn test_rollback_intent() { env.token_a.id(), env.intent.id(), "1", - types::Intent { + Intent { initiator: env.user_id().clone(), send: TokenAmount { token_id: env.token_a.id().clone(), @@ -236,7 +236,7 @@ async fn test_expired_intent(past: Expiration, future: Expiration) { env.token_a.id(), env.intent.id(), "1", - types::Intent { + Intent { initiator: env.user_id().clone(), send: TokenAmount { token_id: env.token_a.id().clone(), @@ -280,7 +280,7 @@ async fn test_expired_intent(past: Expiration, future: Expiration) { env.token_a.id(), env.intent.id(), "2", - types::Intent { + Intent { initiator: env.user_id().clone(), send: TokenAmount { token_id: env.token_a.id().clone(), diff --git a/tests/src/utils/intent.rs b/tests/src/utils/intent.rs index e4ab215..ff8d96b 100644 --- a/tests/src/utils/intent.rs +++ b/tests/src/utils/intent.rs @@ -1,16 +1,16 @@ -use defuse_intent_contract::types; +use defuse_intent_contract::{Action, Intent}; use near_sdk::json_types::U128; use near_workspaces::types::NearToken; use near_workspaces::{Account, AccountId}; use serde_json::json; -pub trait Intent { +pub trait Intending { async fn create_intent( &self, contract_id: &AccountId, intent_account_id: &AccountId, id: &str, - intent: types::Intent, + intent: Intent, ); async fn execute_intent( &self, @@ -23,20 +23,20 @@ pub trait Intent { async fn add_solver(&self, contract_id: &AccountId, solver_id: &AccountId); // View transactions - async fn get_intent(&self, intent_contract_id: &AccountId, id: &str) -> Option; + async fn get_intent(&self, intent_contract_id: &AccountId, id: &str) -> Option; } -impl Intent for Account { +impl Intending for Account { async fn create_intent( &self, contract_id: &AccountId, intent_account_id: &AccountId, id: &str, - intent: types::Intent, + intent: Intent, ) { let amount = intent.send.amount; - let intent = types::intent::Action::CreateIntent((id.to_string(), intent)); - let msg = intent.encode().unwrap(); + let intent = Action::CreateIntent(id.to_string(), intent); + let msg = intent.encode_base64().unwrap(); let args = json!({ "receiver_id": intent_account_id, "amount": amount, @@ -63,8 +63,8 @@ impl Intent for Account { id: &str, amount: U128, ) { - let intent = types::intent::Action::ExecuteIntent(id.to_string()); - let msg = intent.encode().unwrap(); + let intent = Action::ExecuteIntent(id.to_string()); + let msg = intent.encode_base64().unwrap(); let args = json!({ "receiver_id": intent_account_id, "amount": amount, @@ -110,7 +110,7 @@ impl Intent for Account { } // View transactions - async fn get_intent(&self, intent_contract_id: &AccountId, id: &str) -> Option { + async fn get_intent(&self, intent_contract_id: &AccountId, id: &str) -> Option { let result = self .call(intent_contract_id, "get_intent") .args_json(json!({ From 3760dce036a4f4c4e0f4ee04a3c7961f089c2cdd Mon Sep 17 00:00:00 2001 From: De Fuse Date: Thu, 13 Jun 2024 16:43:15 +0400 Subject: [PATCH 2/8] fix: avoid setting global RUSTFLAGS to not mess with rust-analyzer --- Cargo.lock | 1 + Cargo.toml | 2 ++ Makefile.toml | 1 - tests/Cargo.toml | 5 ++++- tests/src/utils/sandbox.rs | 32 ++++++++++++++++++++++---------- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4989bf..60f536e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -898,6 +898,7 @@ version = "0.1.0" dependencies = [ "anyhow", "defuse-intent-contract", + "lazy_static", "near-sdk", "near-workspaces", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 73c8541..f6fc982 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ near-workspaces = "0.10" near-contract-standards = "5.1.0" anyhow = "1" +lazy_static = "1.4" serde_json = "1" strum = { version = "0.26", features = ["derive"] } tokio = { version = "1.38", default-features = false } @@ -43,5 +44,6 @@ codegen-units = 1 opt-level = "z" lto = true debug = false +strip = "symbols" panic = "abort" overflow-checks = true diff --git a/Makefile.toml b/Makefile.toml index 3e8bc51..e1394d2 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -3,7 +3,6 @@ default_to_workspace = false skip_core_tasks = true [env] -RUSTFLAGS = "-C link-arg=-s" TARGET = "wasm32-unknown-unknown" TARGET_DIR = "${PWD}/res" ACCOUNT_WASM_FILE = "defuse-account-contract.wasm" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 7705233..65cc8bb 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -10,7 +10,10 @@ workspace = true defuse-intent-contract = { path = "../intent" } anyhow.workspace = true -near-sdk = { workspace = true, features = ["unit-testing"] } +lazy_static.workspace = true near-workspaces.workspace = true serde_json.workspace = true tokio = { workspace = true, features = ["macros"] } + +[dev-dependencies] +near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/tests/src/utils/sandbox.rs b/tests/src/utils/sandbox.rs index 34bf941..d96ddc9 100644 --- a/tests/src/utils/sandbox.rs +++ b/tests/src/utils/sandbox.rs @@ -1,11 +1,23 @@ -use near_workspaces::types::NearToken; -use near_workspaces::{Account, AccountId, Contract, Worker}; +use std::fs; +use std::path::Path; + +use lazy_static::lazy_static; +use near_workspaces::{types::NearToken, Account, AccountId, Contract, Worker}; use serde_json::json; -const ACCOUNT_WASM: &[u8] = include_bytes!("../../../res/defuse-account-contract.wasm"); -const CONTROLLER_WASM: &[u8] = include_bytes!("../../../res/defuse-controller-contract.wasm"); -const INTENT_WASM: &[u8] = include_bytes!("../../../res/defuse-intent-contract.wasm"); -const TOKEN_WASM: &[u8] = include_bytes!("../../../res/fungible-token.wasm"); +fn read_wasm(name: impl AsRef) -> Vec { + let filename = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../res/") + .join(name) + .with_extension("wasm"); + fs::read(&filename).expect(&format!("{}", filename.display())) +} +lazy_static! { + static ref ACCOUNT_WASM: Vec = read_wasm("defuse-account-contract"); + static ref CONTROLLER_WASM: Vec = read_wasm("defuse-controller-contract"); + static ref INTENT_WASM: Vec = read_wasm("defuse-intent-contract"); + static ref FUNGIBLE_TOKEN_WASM: Vec = read_wasm("fungible-token"); +} const TOTAL_SUPPLY: u128 = 1_000_000_000; @@ -55,7 +67,7 @@ impl Sandbox { } pub async fn deploy_account_contract(&self) -> Contract { - let contract = self.deploy_contract("account", ACCOUNT_WASM).await; + let contract = self.deploy_contract("account", &ACCOUNT_WASM).await; let result = contract .call("new") .args_json(json!({ @@ -72,7 +84,7 @@ impl Sandbox { } pub async fn deploy_controller_contract(&self) -> Contract { - let contract = self.deploy_contract("controller", CONTROLLER_WASM).await; + let contract = self.deploy_contract("controller", &CONTROLLER_WASM).await; let result = contract .call("new") .args_json(json!({ @@ -88,7 +100,7 @@ impl Sandbox { } pub async fn deploy_intent_contract(&self) -> Contract { - let contract = self.deploy_contract("intent", INTENT_WASM).await; + let contract = self.deploy_contract("intent", &INTENT_WASM).await; let result = contract .call("new") .args_json(json!({ @@ -104,7 +116,7 @@ impl Sandbox { } pub async fn deploy_token(&self, token: &str) -> Contract { - let contract = self.deploy_contract(token, TOKEN_WASM).await; + let contract = self.deploy_contract(token, &FUNGIBLE_TOKEN_WASM).await; let result = contract .call("new") .args_json(json!({ From 21ddb02e4a90b386ddbd6522d3a4a5da19251dec Mon Sep 17 00:00:00 2001 From: De Fuse Date: Thu, 13 Jun 2024 19:07:16 +0400 Subject: [PATCH 3/8] feat: use strum::IntoStaticStr to reduce WASM size and for better readability --- Cargo.lock | 3 ++- Cargo.toml | 3 +++ Makefile.toml | 2 +- account-impl/Cargo.toml | 2 ++ account-impl/src/{types => }/account_db.rs | 4 +++- account-impl/src/error.rs | 26 ++++++++++------------ account-impl/src/lib.rs | 14 ++++++------ account-impl/src/types/account.rs | 1 - account-impl/src/types/mod.rs | 4 ---- account/src/lib.rs | 2 +- contracts/README.md | 3 +++ intent-impl/src/lib.rs | 4 ++-- tests/Cargo.toml | 8 +++---- tests/src/tests/intent/env.rs | 5 ++--- tests/src/tests/intent/mod.rs | 11 +++++---- tests/src/utils/intent.rs | 2 +- tests/src/utils/mod.rs | 6 ++--- 17 files changed, 50 insertions(+), 50 deletions(-) rename account-impl/src/{types => }/account_db.rs (99%) delete mode 100644 account-impl/src/types/account.rs delete mode 100644 account-impl/src/types/mod.rs create mode 100644 contracts/README.md diff --git a/Cargo.lock b/Cargo.lock index 60f536e..b4f5da2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,6 +890,7 @@ version = "0.1.0" dependencies = [ "defuse-account-contract", "near-sdk", + "strum 0.26.2", ] [[package]] @@ -897,7 +898,7 @@ name = "defuse-contract-tests" version = "0.1.0" dependencies = [ "anyhow", - "defuse-intent-contract", + "defuse-contracts", "lazy_static", "near-sdk", "near-workspaces", diff --git a/Cargo.toml b/Cargo.toml index f6fc982..89cf987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "intent-impl", "tests", ] +default-members = ["contracts"] [workspace.package] edition = "2021" @@ -47,3 +48,5 @@ debug = false strip = "symbols" panic = "abort" overflow-checks = true + +# 164279 diff --git a/Makefile.toml b/Makefile.toml index e1394d2..3431c5c 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -30,7 +30,7 @@ alias = "tests" [tasks.tests] dependencies = ["build"] command = "cargo" -args = ["test", "--all-targets"] +args = ["test", "--workspace", "--all-targets"] [tasks.build-account] command = "cargo" diff --git a/account-impl/Cargo.toml b/account-impl/Cargo.toml index 404e0bc..06ea02d 100644 --- a/account-impl/Cargo.toml +++ b/account-impl/Cargo.toml @@ -11,7 +11,9 @@ workspace = true [dependencies] defuse-account-contract.workspace = true + near-sdk.workspace = true +strum.workspace = true [dev-dependencies] near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/account-impl/src/types/account_db.rs b/account-impl/src/account_db.rs similarity index 99% rename from account-impl/src/types/account_db.rs rename to account-impl/src/account_db.rs index e2a8a7a..486ea8a 100644 --- a/account-impl/src/types/account_db.rs +++ b/account-impl/src/account_db.rs @@ -1,6 +1,8 @@ +use std::collections::HashMap; + use defuse_account_contract::Account; + use near_sdk::{near, store::LookupMap, AccountId, IntoStorageKey}; -use std::collections::HashMap; use crate::error::Error; diff --git a/account-impl/src/error.rs b/account-impl/src/error.rs index 5d47799..ca4775e 100644 --- a/account-impl/src/error.rs +++ b/account-impl/src/error.rs @@ -1,20 +1,15 @@ -#[derive(Debug)] +use strum::IntoStaticStr; + +#[derive(Debug, IntoStaticStr)] pub enum Error { + #[strum(serialize = "account doesn't exist")] AccountExist, + #[strum(serialize = "user doesn't have any accounts")] EmptyAccounts, + #[strum(serialize = "account doesn't belong to the user")] NoAccount, } -impl AsRef for Error { - fn as_ref(&self) -> &str { - match self { - Self::AccountExist => "Account doesn't exist", - Self::EmptyAccounts => "User doesn't have accounts", - Self::NoAccount => "Account doesn't belong to the user", - } - } -} - #[allow(clippy::module_name_repetitions)] pub trait LogError { fn log_error(self) -> T; @@ -22,15 +17,18 @@ pub trait LogError { impl LogError for Result where - E: AsRef, + E: Into<&'static str>, { #[cfg(target_arch = "wasm32")] + #[inline] fn log_error(self) -> T { - self.unwrap_or_else(|e| near_sdk::env::panic_str(e.as_ref())) + self.map_err(Into::into) + .unwrap_or_else(|e| near_sdk::env::panic_str(e)) } #[cfg(not(target_arch = "wasm32"))] + #[inline] fn log_error(self) -> T { - self.unwrap_or_else(|e| panic!("{}", e.as_ref())) + self.map_err(Into::into).unwrap_or_else(|e| panic!("{e}")) } } diff --git a/account-impl/src/lib.rs b/account-impl/src/lib.rs index ce87fad..4310396 100644 --- a/account-impl/src/lib.rs +++ b/account-impl/src/lib.rs @@ -1,14 +1,14 @@ +mod account_db; +mod error; + use defuse_account_contract::{Account, AccountContract}; -use near_sdk::store::LookupSet; + use near_sdk::{ - env, ext_contract, near, AccountId, BorshStorageKey, PanicOnDefault, PromiseOrValue, + env, ext_contract, near, store::LookupSet, AccountId, BorshStorageKey, PanicOnDefault, + PromiseOrValue, }; -use crate::error::LogError; -use crate::types::AccountDb; - -mod error; -mod types; +use self::{account_db::AccountDb, error::LogError}; #[derive(BorshStorageKey)] #[near(serializers=[borsh])] diff --git a/account-impl/src/types/account.rs b/account-impl/src/types/account.rs deleted file mode 100644 index 8b13789..0000000 --- a/account-impl/src/types/account.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/account-impl/src/types/mod.rs b/account-impl/src/types/mod.rs deleted file mode 100644 index 8e9fede..0000000 --- a/account-impl/src/types/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use account_db::AccountDb; - -mod account; -mod account_db; diff --git a/account/src/lib.rs b/account/src/lib.rs index 0a8e018..0a52aa2 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -16,6 +16,6 @@ pub trait AccountContract { /// Return all [`Account`]s owned by given `owner` fn get_accounts(&self, owner: &AccountId) -> Vec<(String, Account)>; - /// Return MPC contract for this + /// Return MPC contract for this fn mpc_contract(&self) -> &AccountId; } diff --git a/contracts/README.md b/contracts/README.md new file mode 100644 index 0000000..9993190 --- /dev/null +++ b/contracts/README.md @@ -0,0 +1,3 @@ +# Defuse Contracts + +This is a collection of all interfaces of Defuse smart-contracts \ No newline at end of file diff --git a/intent-impl/src/lib.rs b/intent-impl/src/lib.rs index c9a2329..084e951 100644 --- a/intent-impl/src/lib.rs +++ b/intent-impl/src/lib.rs @@ -1,6 +1,6 @@ use defuse_intent_contract::{Action, Intent, IntentContract}; -use near_contract_standards::fungible_token::core::ext_ft_core; -use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; + +use near_contract_standards::fungible_token::{core::ext_ft_core, receiver::FungibleTokenReceiver}; use near_sdk::{ env::{self, panic_str}, json_types::U128, diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 65cc8bb..82dba21 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -6,14 +6,12 @@ edition.workspace = true [lints] workspace = true -[dependencies] -defuse-intent-contract = { path = "../intent" } +[dev-dependencies] +defuse-contracts.workspace = true anyhow.workspace = true lazy_static.workspace = true +near-sdk = { workspace = true, features = ["unit-testing"] } near-workspaces.workspace = true serde_json.workspace = true tokio = { workspace = true, features = ["macros"] } - -[dev-dependencies] -near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/tests/src/tests/intent/env.rs b/tests/src/tests/intent/env.rs index 63969a9..6d92fc3 100644 --- a/tests/src/tests/intent/env.rs +++ b/tests/src/tests/intent/env.rs @@ -1,9 +1,8 @@ -use crate::utils::intent::Intending; -use crate::utils::token::Token; -use crate::utils::Sandbox; use near_sdk::AccountId; use near_workspaces::{Account, Contract}; +use crate::utils::{intent::Intending, token::Token, Sandbox}; + pub struct Env { pub sandbox: Sandbox, pub token_a: Contract, diff --git a/tests/src/tests/intent/mod.rs b/tests/src/tests/intent/mod.rs index 924950d..879a406 100644 --- a/tests/src/tests/intent/mod.rs +++ b/tests/src/tests/intent/mod.rs @@ -1,11 +1,10 @@ -use defuse_intent_contract::{Expiration, Intent, TokenAmount}; +mod env; -use crate::{ - tests::intent::env::Env, - utils::{intent::Intending, token::Token}, -}; +use defuse_contracts::intent::{Expiration, Intent, TokenAmount}; -mod env; +use crate::utils::{intent::Intending, token::Token}; + +use self::env::Env; #[tokio::test] async fn test_generic_successful_flow() { diff --git a/tests/src/utils/intent.rs b/tests/src/utils/intent.rs index ff8d96b..3f09951 100644 --- a/tests/src/utils/intent.rs +++ b/tests/src/utils/intent.rs @@ -1,4 +1,4 @@ -use defuse_intent_contract::{Action, Intent}; +use defuse_contracts::intent::{Action, Intent}; use near_sdk::json_types::U128; use near_workspaces::types::NearToken; use near_workspaces::{Account, AccountId}; diff --git a/tests/src/utils/mod.rs b/tests/src/utils/mod.rs index e8772e2..f7edc06 100644 --- a/tests/src/utils/mod.rs +++ b/tests/src/utils/mod.rs @@ -1,6 +1,6 @@ -pub use sandbox::Sandbox; - -mod account; +pub mod account; pub mod intent; mod sandbox; pub mod token; + +pub use sandbox::Sandbox; From 017bbdcafbe4a0830793bd0fff1011c5f3a9a5f1 Mon Sep 17 00:00:00 2001 From: De Fuse Date: Thu, 13 Jun 2024 19:11:40 +0400 Subject: [PATCH 4/8] chore: make clippy happy --- tests/src/utils/sandbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/utils/sandbox.rs b/tests/src/utils/sandbox.rs index d96ddc9..d3bf288 100644 --- a/tests/src/utils/sandbox.rs +++ b/tests/src/utils/sandbox.rs @@ -10,7 +10,7 @@ fn read_wasm(name: impl AsRef) -> Vec { .join("../res/") .join(name) .with_extension("wasm"); - fs::read(&filename).expect(&format!("{}", filename.display())) + fs::read(filename).unwrap() } lazy_static! { static ref ACCOUNT_WASM: Vec = read_wasm("defuse-account-contract"); From e57433c409724ebc141f584dabd6e047711f4137 Mon Sep 17 00:00:00 2001 From: De Fuse Date: Fri, 14 Jun 2024 20:00:15 +0400 Subject: [PATCH 5/8] fix: move all contract traits to the single crate --- Cargo.lock | 512 ++++++++++++------ Cargo.toml | 28 +- Makefile.toml | 12 +- account-impl/Cargo.toml | 19 - account-impl/src/error.rs | 34 -- account-impl/src/lib.rs | 98 ---- account/Cargo.toml | 12 + {account-impl => account}/README.md | 0 account/src/account.rs | 7 - {account-impl => account}/src/account_db.rs | 5 +- account/src/error.rs | 11 + account/src/lib.rs | 101 +++- contracts/Cargo.toml | 13 +- contracts/README.md | 2 +- contracts/src/account.rs | 23 + contracts/src/controller.rs | 4 + contracts/src/intent/error.rs | 10 + .../src => contracts/src/intent}/intent.rs | 10 +- contracts/src/intent/mod.rs | 27 + contracts/src/lib.rs | 11 +- contracts/src/mpc.rs | 11 + controller-impl/Cargo.toml | 12 - controller-impl/src/lib.rs | 21 - controller/Cargo.toml | 6 +- controller/README.md | 3 - controller/src/lib.rs | 23 +- intent-impl/Cargo.toml | 15 - intent-impl/src/lib.rs | 184 ------- intent/Cargo.toml | 10 +- {intent-impl => intent}/README.md | 0 intent/src/error.rs | 20 - intent/src/lib.rs | 191 ++++++- tests/Cargo.toml | 2 +- tests/src/utils/intent.rs | 7 +- 34 files changed, 788 insertions(+), 656 deletions(-) delete mode 100644 account-impl/Cargo.toml delete mode 100644 account-impl/src/error.rs delete mode 100644 account-impl/src/lib.rs rename {account-impl => account}/README.md (100%) delete mode 100644 account/src/account.rs rename {account-impl => account}/src/account_db.rs (98%) create mode 100644 account/src/error.rs create mode 100644 contracts/src/account.rs create mode 100644 contracts/src/controller.rs create mode 100644 contracts/src/intent/error.rs rename {intent/src => contracts/src/intent}/intent.rs (87%) create mode 100644 contracts/src/intent/mod.rs create mode 100644 contracts/src/mpc.rs delete mode 100644 controller-impl/Cargo.toml delete mode 100644 controller-impl/src/lib.rs delete mode 100644 controller/README.md delete mode 100644 intent-impl/Cargo.toml delete mode 100644 intent-impl/src/lib.rs rename {intent-impl => intent}/README.md (100%) delete mode 100644 intent/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index b4f5da2..0e79562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "actix" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb72882332b6d6282f428b77ba0358cb2687e61a6f6df6a6d3871e8a177c2d4f" +checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" dependencies = [ "actix-macros", "actix-rt", @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ "futures-core", "tokio", @@ -66,11 +66,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "gimli 0.28.1", + "gimli 0.29.0", ] [[package]] @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -253,9 +253,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -364,11 +364,11 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe5b10e214954177fb1dc9fbd20a1a2608fe99e6c832033bdc7cea287a20d77" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" dependencies = [ - "borsh-derive 1.5.0", + "borsh-derive 1.5.1", "cfg_aliases", ] @@ -387,9 +387,9 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a8646f94ab393e43e8b35a2558b1624bed28b97ee09c5d15456e3c9463f46d" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ "once_cell", "proc-macro-crate 3.1.0", @@ -561,9 +561,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -584,9 +584,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" @@ -641,23 +641,23 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", - "clap_derive 4.5.4", + "clap_derive 4.5.5", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.0", + "clap_lex 0.7.1", "strsim 0.11.1", ] @@ -676,9 +676,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -697,9 +697,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -881,20 +881,22 @@ dependencies = [ name = "defuse-account-contract" version = "0.1.0" dependencies = [ + "defuse-contracts", "near-sdk", + "thiserror", ] [[package]] -name = "defuse-account-contract-impl" +name = "defuse-contracts" version = "0.1.0" dependencies = [ - "defuse-account-contract", + "near-contract-standards", "near-sdk", - "strum 0.26.2", + "thiserror", ] [[package]] -name = "defuse-contract-tests" +name = "defuse-contracts-tests" version = "0.1.0" dependencies = [ "anyhow", @@ -906,28 +908,11 @@ dependencies = [ "tokio", ] -[[package]] -name = "defuse-contracts" -version = "0.1.0" -dependencies = [ - "defuse-account-contract", - "defuse-controller-contract", - "defuse-intent-contract", -] - [[package]] name = "defuse-controller-contract" version = "0.1.0" dependencies = [ - "near-sdk", -] - -[[package]] -name = "defuse-controller-contract-impl" -version = "0.1.0" -dependencies = [ - "defuse-account-contract", - "defuse-controller-contract", + "defuse-contracts", "near-sdk", ] @@ -935,16 +920,7 @@ dependencies = [ name = "defuse-intent-contract" version = "0.1.0" dependencies = [ - "near-contract-standards", - "near-sdk", - "strum 0.26.2", -] - -[[package]] -name = "defuse-intent-contract-impl" -version = "0.1.0" -dependencies = [ - "defuse-intent-contract", + "defuse-contracts", "near-contract-standards", "near-sdk", ] @@ -1024,6 +1000,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "dmsort" version = "1.0.2" @@ -1359,9 +1346,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "goblin" @@ -1501,9 +1488,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -1519,9 +1506,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -1589,6 +1576,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1597,12 +1702,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1775,6 +1882,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -1802,9 +1915,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1903,7 +2016,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35cbb989542587b47205e608324ddd391f0cee1c22b4b64ae49f458334b95907" dependencies = [ - "borsh 1.5.0", + "borsh 1.5.1", "serde", ] @@ -1958,7 +2071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2991d2912218a80ec0733ac87f84fa803accea105611eea209d4419271957667" dependencies = [ "blake2", - "borsh 1.5.0", + "borsh 1.5.1", "bs58 0.4.0", "c2-chacha", "curve25519-dalek", @@ -1993,7 +2106,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14e75c875026229902d065e4435804497337b631ec69ba746b102954273e9ad1" dependencies = [ - "borsh 1.5.0", + "borsh 1.5.1", "schemars", "serde", ] @@ -2004,7 +2117,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18ad81e015f7aced8925d5b9ba3f369b36da9575c15812cfd0786bc1213284ca" dependencies = [ - "borsh 1.5.0", + "borsh 1.5.1", "lazy_static", "log", "near-chain-configs", @@ -2041,7 +2154,7 @@ checksum = "d20762631bc8253030013bbae9b5f0542691edc1aa6722f1e8141cc9b928ae5b" dependencies = [ "actix", "base64 0.21.7", - "clap 4.5.4", + "clap 4.5.7", "near-crypto", "near-fmt", "near-primitives-core", @@ -2068,7 +2181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f16a59b6c3e69b0585be951af6fe42a0ba86c0e207cb8c63badd19efd16680" dependencies = [ "assert_matches", - "borsh 1.5.0", + "borsh 1.5.1", "enum-map", "near-account-id", "near-primitives-core", @@ -2088,7 +2201,7 @@ checksum = "0462b067732132babcc89d5577db3bfcb0a1bcfbaaed3f2db4c11cd033666314" dependencies = [ "arbitrary", "base64 0.21.7", - "borsh 1.5.0", + "borsh 1.5.1", "bytesize", "cfg-if 1.0.0", "chrono", @@ -2130,7 +2243,7 @@ checksum = "8443eb718606f572c438be6321a097a8ebd69f8e48d953885b4f16601af88225" dependencies = [ "arbitrary", "base64 0.21.7", - "borsh 1.5.0", + "borsh 1.5.1", "bs58 0.4.0", "derive_more", "enum-map", @@ -2188,7 +2301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520234cfdf04a805ac2f04715889d096eb83fdd5b99ca7d0f8027ae473f891a8" dependencies = [ "base64 0.21.7", - "borsh 1.5.0", + "borsh 1.5.1", "bs58 0.5.1", "near-account-id", "near-crypto", @@ -2219,7 +2332,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "strum_macros 0.26.2", + "strum_macros 0.26.4", "syn 2.0.66", ] @@ -2241,7 +2354,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b68f3f8a2409f72b43efdbeff8e820b81e70824c49fee8572979d789d1683fb" dependencies = [ - "borsh 1.5.0", + "borsh 1.5.1", "serde", ] @@ -2252,7 +2365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c56c80bdb1954808f59bd36a9112377197b38d424991383bf05f52d0fe2e0da5" dependencies = [ "base64 0.21.7", - "borsh 1.5.0", + "borsh 1.5.1", "ed25519-dalek", "enum-map", "memoffset", @@ -2409,9 +2522,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -2550,7 +2663,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -2742,9 +2855,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -2920,9 +3033,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -2949,14 +3062,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2970,13 +3083,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -2987,9 +3100,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" @@ -3091,14 +3204,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] @@ -3110,13 +3225,20 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -3197,16 +3319,6 @@ dependencies = [ "syn 2.0.66", ] -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "secp256k1" version = "0.27.0" @@ -3528,9 +3640,6 @@ name = "strum" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" -dependencies = [ - "strum_macros 0.26.2", -] [[package]] name = "strum_macros" @@ -3547,11 +3656,11 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -3646,6 +3755,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -3675,9 +3795,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -3772,6 +3892,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -4120,27 +4250,12 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -4161,15 +4276,16 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.1" +version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "flate2", "log", "once_cell", "rustls", + "rustls-pki-types", "rustls-webpki", "url", "webpki-roots", @@ -4177,9 +4293,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -4187,11 +4303,23 @@ dependencies = [ "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -4322,9 +4450,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "wee_alloc" @@ -4548,6 +4679,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -4574,11 +4717,56 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zeroize" -version = "1.3.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zeropool-bn" @@ -4593,6 +4781,28 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "0.5.13" diff --git a/Cargo.toml b/Cargo.toml index 89cf987..aef10b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,6 @@ [workspace] resolver = "2" -members = [ - "account", - "account-impl", - "contracts", - "controller", - "controller-impl", - "intent", - "intent-impl", - "tests", -] +members = ["account", "contracts", "controller", "intent", "tests"] default-members = ["contracts"] [workspace.package] @@ -17,24 +8,19 @@ edition = "2021" [workspace.dependencies] defuse-account-contract.path = "./account" -defuse-account-contract-impl.path = "./account-impl" -defuse-contracts.path = "./contracts" +defuse-contracts = { path = "./contracts", default-features = false } defuse-controller-contract.path = "./controller" -defuse-controller-contract-impl.path = "./controller-impl" defuse-intent-contract.path = "./intent" -defuse-intent-contract-impl.path = "./intent-impl" - -near-sdk = "5.1" -near-workspaces = "0.10" -near-contract-standards = "5.1.0" anyhow = "1" lazy_static = "1.4" +near-contract-standards = "5.1.0" +near-sdk = "5.1" +near-workspaces = "0.10" serde_json = "1" -strum = { version = "0.26", features = ["derive"] } +thiserror = "1" tokio = { version = "1.38", default-features = false } - [workspace.lints.clippy] all = "deny" nursery = "deny" @@ -48,5 +34,3 @@ debug = false strip = "symbols" panic = "abort" overflow-checks = true - -# 164279 diff --git a/Makefile.toml b/Makefile.toml index 3431c5c..ec723b8 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -40,7 +40,7 @@ args = [ "${TARGET}", "--release", "--package", - "defuse-account-contract-impl", + "defuse-account-contract", ] [tasks.build-intent] @@ -51,7 +51,7 @@ args = [ "${TARGET}", "--release", "--package", - "defuse-intent-contract-impl", + "defuse-intent-contract", ] [tasks.build-controller] @@ -62,18 +62,18 @@ args = [ "${TARGET}", "--release", "-p", - "defuse-controller-contract-impl", + "defuse-controller-contract", ] [tasks.cp-contracts] script = """ -cp target/${TARGET}/release/defuse_account_contract_impl.wasm ${TARGET_DIR}/${ACCOUNT_WASM_FILE} -cp target/${TARGET}/release/defuse_intent_contract_impl.wasm ${TARGET_DIR}/${INTENT_WASM_FILE} +cp target/${TARGET}/release/defuse_account_contract.wasm ${TARGET_DIR}/${ACCOUNT_WASM_FILE} +cp target/${TARGET}/release/defuse_intent_contract.wasm ${TARGET_DIR}/${INTENT_WASM_FILE} """ [tasks.cp-controller] script = """ -cp target/${TARGET}/release/defuse_controller_contract_impl.wasm ${TARGET_DIR}/${CONTROLLER_WASM_FILE} +cp target/${TARGET}/release/defuse_controller_contract.wasm ${TARGET_DIR}/${CONTROLLER_WASM_FILE} """ [tasks.clean] diff --git a/account-impl/Cargo.toml b/account-impl/Cargo.toml deleted file mode 100644 index 06ea02d..0000000 --- a/account-impl/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "defuse-account-contract-impl" -version = "0.1.0" -edition.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] - -[lints] -workspace = true - -[dependencies] -defuse-account-contract.workspace = true - -near-sdk.workspace = true -strum.workspace = true - -[dev-dependencies] -near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/account-impl/src/error.rs b/account-impl/src/error.rs deleted file mode 100644 index ca4775e..0000000 --- a/account-impl/src/error.rs +++ /dev/null @@ -1,34 +0,0 @@ -use strum::IntoStaticStr; - -#[derive(Debug, IntoStaticStr)] -pub enum Error { - #[strum(serialize = "account doesn't exist")] - AccountExist, - #[strum(serialize = "user doesn't have any accounts")] - EmptyAccounts, - #[strum(serialize = "account doesn't belong to the user")] - NoAccount, -} - -#[allow(clippy::module_name_repetitions)] -pub trait LogError { - fn log_error(self) -> T; -} - -impl LogError for Result -where - E: Into<&'static str>, -{ - #[cfg(target_arch = "wasm32")] - #[inline] - fn log_error(self) -> T { - self.map_err(Into::into) - .unwrap_or_else(|e| near_sdk::env::panic_str(e)) - } - - #[cfg(not(target_arch = "wasm32"))] - #[inline] - fn log_error(self) -> T { - self.map_err(Into::into).unwrap_or_else(|e| panic!("{e}")) - } -} diff --git a/account-impl/src/lib.rs b/account-impl/src/lib.rs deleted file mode 100644 index 4310396..0000000 --- a/account-impl/src/lib.rs +++ /dev/null @@ -1,98 +0,0 @@ -mod account_db; -mod error; - -use defuse_account_contract::{Account, AccountContract}; - -use near_sdk::{ - env, ext_contract, near, store::LookupSet, AccountId, BorshStorageKey, PanicOnDefault, - PromiseOrValue, -}; - -use self::{account_db::AccountDb, error::LogError}; - -#[derive(BorshStorageKey)] -#[near(serializers=[borsh])] -enum Prefix { - Accounts, - Indexers, -} - -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct AccountContractImpl { - owner_id: AccountId, - /// MPC contract id. - mpc_contract_id: AccountId, - /// List of indexers. Accounts which allow to add a new account. - indexers: LookupSet, - /// Key here is `account_id + '.' + derivation_path` - /// Value is account id of the owner. - accounts: AccountDb, -} - -#[near] -impl AccountContract for AccountContractImpl { - fn create_account(&mut self, account_id: AccountId, derivation_path: String) { - // Only indexers can call this transaction. - let predecessor_id = env::predecessor_account_id(); - self.assert_indexer(&predecessor_id); - - self.accounts - .add_account(account_id, derivation_path, Account::default()) - .log_error(); - } - - fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String) { - self.accounts - .change_owner(from, to, derivation_path) - .log_error(); - } - - fn get_accounts(&self, account_id: &AccountId) -> Vec<(String, Account)> { - self.accounts.get_accounts(account_id).log_error() - } - - fn mpc_contract(&self) -> &AccountId { - &self.mpc_contract_id - } -} - -#[near] -impl AccountContractImpl { - #[init] - #[must_use] - #[allow(clippy::use_self)] - pub fn new(owner_id: AccountId, mpc_contract_id: AccountId) -> Self { - Self { - owner_id, - mpc_contract_id, - indexers: LookupSet::new(Prefix::Indexers), - accounts: AccountDb::new(Prefix::Accounts), - } - } - - #[private] - pub fn set_mpc_contract(&mut self, contract_id: AccountId) { - self.mpc_contract_id = contract_id; - } - - fn assert_indexer(&self, account_id: &AccountId) { - assert!( - self.indexers.contains(account_id), - "Only indexers allow adding an account" - ); - } -} - -#[ext_contract(ext_mpc)] -pub trait MpcRecovery { - fn sign( - &self, - payload: Vec, - path: &str, - key_version: u32, - ) -> PromiseOrValue<(String, String)>; -} - -#[cfg(test)] -mod contract_tests {} diff --git a/account/Cargo.toml b/account/Cargo.toml index 828d0e2..16cfc12 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -3,5 +3,17 @@ name = "defuse-account-contract" version = "0.1.0" edition.workspace = true +[lib] +crate-type = ["cdylib", "rlib"] + +[lints] +workspace = true + [dependencies] +defuse-contracts = { workspace = true, features = ["account"] } + near-sdk.workspace = true +thiserror.workspace = true + +[dev-dependencies] +near-sdk = { workspace = true, features = ["unit-testing"] } diff --git a/account-impl/README.md b/account/README.md similarity index 100% rename from account-impl/README.md rename to account/README.md diff --git a/account/src/account.rs b/account/src/account.rs deleted file mode 100644 index a930f28..0000000 --- a/account/src/account.rs +++ /dev/null @@ -1,7 +0,0 @@ -use near_sdk::near; - -#[derive(Debug, Clone, Default, Eq, PartialEq)] -#[near(serializers=[borsh, json])] -pub struct Account { - is_locked: bool, -} diff --git a/account-impl/src/account_db.rs b/account/src/account_db.rs similarity index 98% rename from account-impl/src/account_db.rs rename to account/src/account_db.rs index 486ea8a..39bf4d2 100644 --- a/account-impl/src/account_db.rs +++ b/account/src/account_db.rs @@ -1,10 +1,9 @@ use std::collections::HashMap; -use defuse_account_contract::Account; - +use defuse_contracts::account::Account; use near_sdk::{near, store::LookupMap, AccountId, IntoStorageKey}; -use crate::error::Error; +use crate::Error; // Accounts that belong user. Key here is derivation path. type UserAccounts = HashMap; diff --git a/account/src/error.rs b/account/src/error.rs new file mode 100644 index 0000000..b373151 --- /dev/null +++ b/account/src/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum Error { + #[error("account doesn't exist")] + AccountExist, + #[error("user doesn't have any accounts")] + EmptyAccounts, + #[error("account doesn't belong to the user")] + NoAccount, +} diff --git a/account/src/lib.rs b/account/src/lib.rs index 0a52aa2..e98b449 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -1,21 +1,82 @@ -mod account; - -pub use self::account::*; - -use near_sdk::{ext_contract, AccountId}; - -// TODO: make this contract an extension of NFT, since every Account -// is a unique item. This will ease the integration of Defuse -// with already existing tooling around NFTs -#[ext_contract(ext_account_contract)] -pub trait AccountContract { - /// Create an account with given defivation path for given owner - fn create_account(&mut self, owner: AccountId, derivation_path: String); - /// Change an owner of account with given derivation path. - fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String); - /// Return all [`Account`]s owned by given `owner` - fn get_accounts(&self, owner: &AccountId) -> Vec<(String, Account)>; - - /// Return MPC contract for this - fn mpc_contract(&self) -> &AccountId; +mod account_db; +mod error; + +use defuse_contracts::account::{Account, AccountContract}; +use near_sdk::{env, near, store::LookupSet, AccountId, BorshStorageKey, PanicOnDefault}; + +use self::account_db::AccountDb; +pub use self::error::*; + +#[derive(BorshStorageKey)] +#[near(serializers=[borsh])] +enum Prefix { + Accounts, + Indexers, +} + +#[near(contract_state)] +#[derive(PanicOnDefault)] +pub struct AccountContractImpl { + owner_id: AccountId, + /// MPC contract id. + mpc_contract_id: AccountId, + /// List of indexers. Accounts which allow to add a new account. + indexers: LookupSet, + /// Key here is `account_id + '.' + derivation_path` + /// Value is account id of the owner. + accounts: AccountDb, +} + +#[near] +impl AccountContract for AccountContractImpl { + fn create_account(&mut self, account_id: AccountId, derivation_path: String) { + // Only indexers can call this transaction. + let predecessor_id = env::predecessor_account_id(); + self.assert_indexer(&predecessor_id); + + self.accounts + .add_account(account_id, derivation_path, Account::default()) + .unwrap(); + } + + fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String) { + self.accounts + .change_owner(from, to, derivation_path) + .unwrap(); + } + + fn get_accounts(&self, account_id: &AccountId) -> Vec<(String, Account)> { + self.accounts.get_accounts(account_id).unwrap() + } + + fn mpc_contract(&self) -> &AccountId { + &self.mpc_contract_id + } +} + +#[near] +impl AccountContractImpl { + #[init] + #[must_use] + #[allow(clippy::use_self)] + pub fn new(owner_id: AccountId, mpc_contract_id: AccountId) -> Self { + Self { + owner_id, + mpc_contract_id, + indexers: LookupSet::new(Prefix::Indexers), + accounts: AccountDb::new(Prefix::Accounts), + } + } + + #[private] + pub fn set_mpc_contract(&mut self, contract_id: AccountId) { + self.mpc_contract_id = contract_id; + } + + fn assert_indexer(&self, account_id: &AccountId) { + assert!( + self.indexers.contains(account_id), + "Only indexers allow adding an account" + ); + } } diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index e66d964..2c111c0 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -4,6 +4,13 @@ version = "0.1.0" edition.workspace = true [dependencies] -defuse-account-contract.workspace = true -defuse-controller-contract.workspace = true -defuse-intent-contract.workspace = true +near-sdk.workspace = true +near-contract-standards.workspace = true +thiserror.workspace = true + +[features] +default = ["account", "controller", "intent", "mpc"] +account = [] +controller = [] +intent = [] +mpc = [] diff --git a/contracts/README.md b/contracts/README.md index 9993190..132593a 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -1,3 +1,3 @@ # Defuse Contracts -This is a collection of all interfaces of Defuse smart-contracts \ No newline at end of file +This is a collection of interfaces for all Defuse smart-contracts \ No newline at end of file diff --git a/contracts/src/account.rs b/contracts/src/account.rs new file mode 100644 index 0000000..3cc612a --- /dev/null +++ b/contracts/src/account.rs @@ -0,0 +1,23 @@ +use near_sdk::{ext_contract, near, AccountId}; + +// TODO: make this contract an extension of NFT, since every Account +// is a unique item. This will ease the integration of Defuse +// with already existing tooling around NFTs +#[ext_contract(ext_account_contract)] +pub trait AccountContract { + /// Create an account with given defivation path for given owner + fn create_account(&mut self, owner: AccountId, derivation_path: String); + /// Change an owner of account with given derivation path. + fn change_owner(&mut self, from: &AccountId, to: AccountId, derivation_path: String); + /// Return all [`Account`]s owned by given `owner` + fn get_accounts(&self, owner: &AccountId) -> Vec<(String, Account)>; + + /// Return MPC contract for this + fn mpc_contract(&self) -> &AccountId; +} + +#[derive(Debug, Clone, Default, Eq, PartialEq)] +#[near(serializers=[borsh, json])] +pub struct Account { + is_locked: bool, +} diff --git a/contracts/src/controller.rs b/contracts/src/controller.rs new file mode 100644 index 0000000..7957286 --- /dev/null +++ b/contracts/src/controller.rs @@ -0,0 +1,4 @@ +use near_sdk::ext_contract; + +#[ext_contract(ext_controller_contract)] +pub trait ControllerContract {} diff --git a/contracts/src/intent/error.rs b/contracts/src/intent/error.rs new file mode 100644 index 0000000..1369a31 --- /dev/null +++ b/contracts/src/intent/error.rs @@ -0,0 +1,10 @@ +use near_sdk::base64; +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum IntentError { + #[error("Borsh")] + Borsh, + #[error("base64: {0}")] + Base64(#[from] base64::DecodeError), +} diff --git a/intent/src/intent.rs b/contracts/src/intent/intent.rs similarity index 87% rename from intent/src/intent.rs rename to contracts/src/intent/intent.rs index 2de12ef..ac465a8 100644 --- a/intent/src/intent.rs +++ b/contracts/src/intent/intent.rs @@ -6,7 +6,7 @@ use near_sdk::{ near, AccountId, }; -use crate::ContractError; +use super::IntentError; #[near(serializers=[borsh])] pub enum Action { @@ -22,12 +22,12 @@ pub enum Action { } impl Action { - pub fn base64_decode(msg: impl AsRef<[u8]>) -> Result { - Self::try_from_slice(&STANDARD.decode(msg)?).map_err(|_| ContractError::BorshDeserialize) + pub fn base64_decode(msg: impl AsRef<[u8]>) -> Result { + Self::try_from_slice(&STANDARD.decode(msg)?).map_err(|_| IntentError::Borsh) } - pub fn encode_base64(&self) -> Result { - Ok(STANDARD.encode(borsh::to_vec(self).map_err(|_| ContractError::BorshSerialize)?)) + pub fn encode_base64(&self) -> Result { + Ok(STANDARD.encode(borsh::to_vec(self).map_err(|_| IntentError::Borsh)?)) } } diff --git a/contracts/src/intent/mod.rs b/contracts/src/intent/mod.rs new file mode 100644 index 0000000..40ebc42 --- /dev/null +++ b/contracts/src/intent/mod.rs @@ -0,0 +1,27 @@ +mod error; +mod intent; + +pub use self::{error::*, intent::*}; + +use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; +use near_sdk::{ext_contract, AccountId, Promise}; + +#[ext_contract(ext_intent_contract)] +pub trait IntentContract: FungibleTokenReceiver { + /// Return pending intent by id. + fn get_intent(&self, id: &String) -> Option<&Intent>; + + /// Rollback created intent and refund tokens to the intent's initiator. + /// The transaction could be called by an intent initiator or owner. + /// + /// # Panics + /// + /// The panic occurs if intent doesn't exist of caller is not allowed. + fn rollback_intent(&mut self, id: &String) -> Promise; + + /// Add a new solver to the whitelist. + fn add_solver(&mut self, solver_id: AccountId); + + /// Check if the provided solver is allowed. + fn is_allowed_solver(&self, solver_id: AccountId) -> bool; +} diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index 968a1e6..226ecd2 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -1,3 +1,8 @@ -pub use defuse_account_contract as account; -pub use defuse_controller_contract as controller; -pub use defuse_intent_contract as intent; +#[cfg(feature = "account")] +pub mod account; +#[cfg(feature = "controller")] +pub mod controller; +#[cfg(feature = "intent")] +pub mod intent; +#[cfg(feature = "mpc")] +pub mod mpc; diff --git a/contracts/src/mpc.rs b/contracts/src/mpc.rs new file mode 100644 index 0000000..ff16a55 --- /dev/null +++ b/contracts/src/mpc.rs @@ -0,0 +1,11 @@ +use near_sdk::{ext_contract, PromiseOrValue}; + +#[ext_contract(ext_mpc)] +pub trait MpcRecovery { + fn sign( + &self, + payload: Vec, + path: &str, + key_version: u32, + ) -> PromiseOrValue<(String, String)>; +} diff --git a/controller-impl/Cargo.toml b/controller-impl/Cargo.toml deleted file mode 100644 index b22454e..0000000 --- a/controller-impl/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "defuse-controller-contract-impl" -version = "0.1.0" -edition.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -defuse-account-contract.workspace = true -defuse-controller-contract.workspace = true -near-sdk.workspace = true diff --git a/controller-impl/src/lib.rs b/controller-impl/src/lib.rs deleted file mode 100644 index bbff652..0000000 --- a/controller-impl/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use defuse_controller_contract::ControllerContract; -use near_sdk::{near, AccountId, PanicOnDefault}; - -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct ControllerContractImpl { - owner_id: AccountId, -} - -#[near] -impl ControllerContract for ControllerContractImpl {} - -#[near] -impl ControllerContractImpl { - #[init] - #[must_use] - #[allow(clippy::use_self)] - pub const fn new(owner_id: AccountId) -> Self { - Self { owner_id } - } -} diff --git a/controller/Cargo.toml b/controller/Cargo.toml index cc041b8..f09c9c2 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -3,8 +3,10 @@ name = "defuse-controller-contract" version = "0.1.0" edition.workspace = true -[lints] -workspace = true +[lib] +crate-type = ["cdylib", "rlib"] [dependencies] +defuse-contracts = { workspace = true, features = ["account", "controller"] } + near-sdk.workspace = true diff --git a/controller/README.md b/controller/README.md deleted file mode 100644 index db97826..0000000 --- a/controller/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Controller contract for the Defuse project - -The main goal of the contract is to manage and update account and intent contracts. diff --git a/controller/src/lib.rs b/controller/src/lib.rs index 7957286..8736cad 100644 --- a/controller/src/lib.rs +++ b/controller/src/lib.rs @@ -1,4 +1,21 @@ -use near_sdk::ext_contract; +use defuse_contracts::controller::ControllerContract; +use near_sdk::{near, AccountId, PanicOnDefault}; -#[ext_contract(ext_controller_contract)] -pub trait ControllerContract {} +#[near(contract_state)] +#[derive(PanicOnDefault)] +pub struct ControllerContractImpl { + owner_id: AccountId, +} + +#[near] +impl ControllerContract for ControllerContractImpl {} + +#[near] +impl ControllerContractImpl { + #[init] + #[must_use] + #[allow(clippy::use_self)] + pub const fn new(owner_id: AccountId) -> Self { + Self { owner_id } + } +} diff --git a/intent-impl/Cargo.toml b/intent-impl/Cargo.toml deleted file mode 100644 index b40b296..0000000 --- a/intent-impl/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "defuse-intent-contract-impl" -version = "0.1.0" -edition.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] - -[lints] -workspace = true - -[dependencies] -defuse-intent-contract.workspace = true -near-sdk.workspace = true -near-contract-standards.workspace = true diff --git a/intent-impl/src/lib.rs b/intent-impl/src/lib.rs deleted file mode 100644 index 084e951..0000000 --- a/intent-impl/src/lib.rs +++ /dev/null @@ -1,184 +0,0 @@ -use defuse_intent_contract::{Action, Intent, IntentContract}; - -use near_contract_standards::fungible_token::{core::ext_ft_core, receiver::FungibleTokenReceiver}; -use near_sdk::{ - env::{self, panic_str}, - json_types::U128, - log, near, - store::{LookupMap, LookupSet}, - AccountId, BorshStorageKey, NearToken, PanicOnDefault, Promise, PromiseOrValue, -}; - -#[derive(BorshStorageKey)] -#[near(serializers=[borsh])] -enum Prefix { - SupportedTokens, - AllowedSolvers, - Intents, -} - -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct IntentContractImpl { - owner_id: AccountId, - supported_tokens: LookupSet, - allowed_solvers: LookupSet, - intents: LookupMap, -} - -#[near] -impl IntentContract for IntentContractImpl { - fn add_solver(&mut self, solver_id: AccountId) { - self.assert_owner(); - self.allowed_solvers.insert(solver_id); - } - - fn rollback_intent(&mut self, id: String) -> Promise { - let intent = self - .intents - .get(&id) - .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); - let predecessor_id = env::predecessor_account_id(); - - assert!( - predecessor_id == intent.initiator - || predecessor_id == self.owner_id - || predecessor_id == env::current_account_id(), - "Only initiator, self or owner can roll back the intent" - ); - - ext_ft_core::ext(intent.send.token_id.clone()) - .with_attached_deposit(NearToken::from_yoctonear(1)) - .ft_transfer(intent.initiator.clone(), intent.send.amount, None) - .then(Self::ext(env::current_account_id()).cleanup_intent(&id)) - } - - fn get_intent(&self, id: String) -> Option<&Intent> { - self.intents.get(&id) - } - - fn is_allowed_solver(&self, solver_id: AccountId) -> bool { - self.allowed_solvers.contains(&solver_id) - } -} - -#[near] -impl FungibleTokenReceiver for IntentContractImpl { - /// The callback is called by NEP-141 after `ft_transfer_call`. - /// - /// # Panics - /// - /// The panic occurs if an attempt to add an intent with an existing id or execute - /// a nonexistent intent. - fn ft_on_transfer( - &mut self, - sender_id: AccountId, - amount: U128, - msg: String, - ) -> PromiseOrValue { - // Validate that sender_id is in white token list. - // self.assert_token(&sender_id); // TODO: Check if we need tokens validation. - - let action = Action::base64_decode(&msg).expect("decode Action"); - - log!(format!("{sender_id} : {}: msg: {msg}", amount.0)); - - match action { - Action::CreateIntent(id, intent) => { - assert!( - self.intents.insert(id, intent).is_none(), - "Intent already exists" - ); - - PromiseOrValue::Value(0.into()) - } - Action::ExecuteIntent(id) => { - let current_id = env::current_account_id(); - let solver_id = env::signer_account_id(); - self.assert_solver(&solver_id); - - let intent = self - .intents - .get(&id) - .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); - - let promise = if intent.is_expired() { - Self::ext(current_id).rollback_intent(id) - } else { - ext_ft_core::ext(intent.send.token_id.clone()) - .with_attached_deposit(NearToken::from_yoctonear(1)) - .ft_transfer(solver_id, intent.send.amount, None) - .then( - ext_ft_core::ext(intent.receive.token_id.clone()) - .with_attached_deposit(NearToken::from_yoctonear(1)) - .ft_transfer(intent.initiator.clone(), intent.receive.amount, None), - ) - .then(Self::ext(current_id).cleanup_intent(&id)) - }; - - PromiseOrValue::Promise(promise) - } - } - } -} - -#[near] -impl IntentContractImpl { - /// Contract constructor. - #[init] - #[must_use] - #[allow(clippy::use_self)] - pub fn new(owner_id: AccountId) -> Self { - Self { - owner_id, - supported_tokens: LookupSet::new(Prefix::SupportedTokens), - allowed_solvers: LookupSet::new(Prefix::AllowedSolvers), - intents: LookupMap::new(Prefix::Intents), - } - } - - /// Callback which removes an intent after successful execution. - #[private] - pub fn cleanup_intent(&mut self, intent_id: &String) { - self.intents.remove(intent_id); - } - - /// Set a new owner of the contract. - #[inline] - pub fn set_owner(&mut self, owner_id: AccountId) { - self.assert_owner(); - self.owner_id = owner_id; - } - - /// Return owner of the contract. - #[inline] - pub const fn get_owner(&self) -> &AccountId { - &self.owner_id - } - - #[inline] - fn assert_owner(&self) { - assert_eq!( - self.owner_id, - env::predecessor_account_id(), - "Only owner is allowed to add a new solver" - ); - } - - #[inline] - fn assert_solver(&self, solver_id: &AccountId) { - assert!( - self.allowed_solvers.contains(solver_id), - "The solver is not allowed" - ); - } - - #[allow(dead_code)] - #[inline] - fn assert_token(&self, token_id: &AccountId) { - assert!( - self.supported_tokens.contains(token_id.as_str()), - "Unsupported token" - ); - } -} diff --git a/intent/Cargo.toml b/intent/Cargo.toml index c0eef77..e6986f6 100644 --- a/intent/Cargo.toml +++ b/intent/Cargo.toml @@ -3,8 +3,14 @@ name = "defuse-intent-contract" version = "0.1.0" edition.workspace = true +[lib] +crate-type = ["cdylib", "rlib"] + +[lints] +workspace = true + [dependencies] +defuse-contracts = { workspace = true, features = ["intent"] } + near-sdk.workspace = true near-contract-standards.workspace = true - -strum.workspace = true \ No newline at end of file diff --git a/intent-impl/README.md b/intent/README.md similarity index 100% rename from intent-impl/README.md rename to intent/README.md diff --git a/intent/src/error.rs b/intent/src/error.rs deleted file mode 100644 index e98744b..0000000 --- a/intent/src/error.rs +++ /dev/null @@ -1,20 +0,0 @@ -use near_sdk::base64; -use strum::IntoStaticStr; - -#[allow(clippy::module_name_repetitions)] -#[derive(Debug, IntoStaticStr)] -#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] -pub enum ContractError { - #[strum(serialize = "BORSH_SERIALIZE_ERROR")] - BorshSerialize, - #[strum(serialize = "BORSH_DESERIALIZE_ERROR")] - BorshDeserialize, - #[strum(serialize = "BASE64_DECODE_ERROR")] - Base64Decode, -} - -impl From for ContractError { - fn from(_: base64::DecodeError) -> Self { - Self::Base64Decode - } -} diff --git a/intent/src/lib.rs b/intent/src/lib.rs index ab7e82e..c7735d1 100644 --- a/intent/src/lib.rs +++ b/intent/src/lib.rs @@ -1,27 +1,184 @@ -mod error; -mod intent; +use defuse_contracts::intent::{Action, Intent, IntentContract}; -pub use self::{error::*, intent::*}; +use near_contract_standards::fungible_token::{core::ext_ft_core, receiver::FungibleTokenReceiver}; +use near_sdk::{ + env::{self, panic_str}, + json_types::U128, + log, near, + store::{LookupMap, LookupSet}, + AccountId, BorshStorageKey, NearToken, PanicOnDefault, Promise, PromiseOrValue, +}; -use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; -use near_sdk::{ext_contract, AccountId, Promise}; +#[derive(BorshStorageKey)] +#[near(serializers=[borsh])] +enum Prefix { + SupportedTokens, + AllowedSolvers, + Intents, +} + +#[near(contract_state)] +#[derive(PanicOnDefault)] +pub struct IntentContractImpl { + owner_id: AccountId, + supported_tokens: LookupSet, + allowed_solvers: LookupSet, + intents: LookupMap, +} + +#[near] +impl IntentContract for IntentContractImpl { + fn add_solver(&mut self, solver_id: AccountId) { + self.assert_owner(); + self.allowed_solvers.insert(solver_id); + } + + fn rollback_intent(&mut self, id: &String) -> Promise { + let intent = self + .intents + .get(id) + .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); + let predecessor_id = env::predecessor_account_id(); + + assert!( + predecessor_id == intent.initiator + || predecessor_id == self.owner_id + || predecessor_id == env::current_account_id(), + "Only initiator, self or owner can roll back the intent" + ); -#[ext_contract(ext_intent_contract)] -pub trait IntentContract: FungibleTokenReceiver { - /// Return pending intent by id. - fn get_intent(&self, id: String) -> Option<&Intent>; + ext_ft_core::ext(intent.send.token_id.clone()) + .with_attached_deposit(NearToken::from_yoctonear(1)) + .ft_transfer(intent.initiator.clone(), intent.send.amount, None) + .then(Self::ext(env::current_account_id()).cleanup_intent(id)) + } - /// Rollback created intent and refund tokens to the intent's initiator. - /// The transaction could be called by an intent initiator or owner. + fn get_intent(&self, id: &String) -> Option<&Intent> { + self.intents.get(id) + } + + fn is_allowed_solver(&self, solver_id: AccountId) -> bool { + self.allowed_solvers.contains(&solver_id) + } +} + +#[near] +impl FungibleTokenReceiver for IntentContractImpl { + /// The callback is called by NEP-141 after `ft_transfer_call`. /// /// # Panics /// - /// The panic occurs if intent doesn't exist of caller is not allowed. - fn rollback_intent(&mut self, id: String) -> Promise; + /// The panic occurs if an attempt to add an intent with an existing id or execute + /// a nonexistent intent. + fn ft_on_transfer( + &mut self, + sender_id: AccountId, + amount: U128, + msg: String, + ) -> PromiseOrValue { + // Validate that sender_id is in white token list. + // self.assert_token(&sender_id); // TODO: Check if we need tokens validation. + + let action = Action::base64_decode(&msg).expect("decode Action"); + + log!(format!("{sender_id} : {}: msg: {msg}", amount.0)); + + match action { + Action::CreateIntent(id, intent) => { + assert!( + self.intents.insert(id, intent).is_none(), + "Intent already exists" + ); + + PromiseOrValue::Value(0.into()) + } + Action::ExecuteIntent(id) => { + let current_id = env::current_account_id(); + let solver_id = env::signer_account_id(); + self.assert_solver(&solver_id); + + let intent = self + .intents + .get(&id) + .unwrap_or_else(|| panic_str(&format!("No intent for id: {id}"))); + + let promise = if intent.is_expired() { + Self::ext(current_id).rollback_intent(&id) + } else { + ext_ft_core::ext(intent.send.token_id.clone()) + .with_attached_deposit(NearToken::from_yoctonear(1)) + .ft_transfer(solver_id, intent.send.amount, None) + .then( + ext_ft_core::ext(intent.receive.token_id.clone()) + .with_attached_deposit(NearToken::from_yoctonear(1)) + .ft_transfer(intent.initiator.clone(), intent.receive.amount, None), + ) + .then(Self::ext(current_id).cleanup_intent(&id)) + }; + + PromiseOrValue::Promise(promise) + } + } + } +} + +#[near] +impl IntentContractImpl { + /// Contract constructor. + #[init] + #[must_use] + #[allow(clippy::use_self)] + pub fn new(owner_id: AccountId) -> Self { + Self { + owner_id, + supported_tokens: LookupSet::new(Prefix::SupportedTokens), + allowed_solvers: LookupSet::new(Prefix::AllowedSolvers), + intents: LookupMap::new(Prefix::Intents), + } + } + + /// Callback which removes an intent after successful execution. + #[private] + pub fn cleanup_intent(&mut self, intent_id: &String) { + self.intents.remove(intent_id); + } + + /// Set a new owner of the contract. + #[inline] + pub fn set_owner(&mut self, owner_id: AccountId) { + self.assert_owner(); + self.owner_id = owner_id; + } + + /// Return owner of the contract. + #[inline] + pub const fn get_owner(&self) -> &AccountId { + &self.owner_id + } + + #[inline] + fn assert_owner(&self) { + assert_eq!( + self.owner_id, + env::predecessor_account_id(), + "Only owner is allowed to add a new solver" + ); + } - /// Add a new solver to the whitelist. - fn add_solver(&mut self, solver_id: AccountId); + #[inline] + fn assert_solver(&self, solver_id: &AccountId) { + assert!( + self.allowed_solvers.contains(solver_id), + "The solver is not allowed" + ); + } - /// Check if the provided solver is allowed. - fn is_allowed_solver(&self, solver_id: AccountId) -> bool; + #[allow(dead_code)] + #[inline] + fn assert_token(&self, token_id: &AccountId) { + assert!( + self.supported_tokens.contains(token_id.as_str()), + "Unsupported token" + ); + } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 82dba21..5963adc 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "defuse-contract-tests" +name = "defuse-contracts-tests" version = "0.1.0" edition.workspace = true diff --git a/tests/src/utils/intent.rs b/tests/src/utils/intent.rs index 3f09951..f5df8b6 100644 --- a/tests/src/utils/intent.rs +++ b/tests/src/utils/intent.rs @@ -1,7 +1,6 @@ use defuse_contracts::intent::{Action, Intent}; use near_sdk::json_types::U128; -use near_workspaces::types::NearToken; -use near_workspaces::{Account, AccountId}; +use near_workspaces::{types::NearToken, Account, AccountId}; use serde_json::json; pub trait Intending { @@ -36,7 +35,7 @@ impl Intending for Account { ) { let amount = intent.send.amount; let intent = Action::CreateIntent(id.to_string(), intent); - let msg = intent.encode_base64().unwrap(); + let msg = intent.encode_base64().expect("encode Action"); let args = json!({ "receiver_id": intent_account_id, "amount": amount, @@ -64,7 +63,7 @@ impl Intending for Account { amount: U128, ) { let intent = Action::ExecuteIntent(id.to_string()); - let msg = intent.encode_base64().unwrap(); + let msg = intent.encode_base64().expect("encode Action"); let args = json!({ "receiver_id": intent_account_id, "amount": amount, From a0fe50d042b6c9d03ffa4de88fae531e058a01ba Mon Sep 17 00:00:00 2001 From: De Fuse Date: Mon, 17 Jun 2024 20:40:25 +0400 Subject: [PATCH 6/8] fix: CR comments --- account/src/lib.rs | 6 +++--- contracts/src/intent/mod.rs | 21 +++++++++++---------- intent/src/lib.rs | 2 +- tests/src/tests/account/mod.rs | 3 ++- tests/src/utils/intent.rs | 4 ++-- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/account/src/lib.rs b/account/src/lib.rs index e98b449..eb154d9 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -1,12 +1,12 @@ -mod account_db; -mod error; - use defuse_contracts::account::{Account, AccountContract}; use near_sdk::{env, near, store::LookupSet, AccountId, BorshStorageKey, PanicOnDefault}; use self::account_db::AccountDb; pub use self::error::*; +mod account_db; +mod error; + #[derive(BorshStorageKey)] #[near(serializers=[borsh])] enum Prefix { diff --git a/contracts/src/intent/mod.rs b/contracts/src/intent/mod.rs index 2e529d0..2fb6e3c 100644 --- a/contracts/src/intent/mod.rs +++ b/contracts/src/intent/mod.rs @@ -1,7 +1,3 @@ -mod error; - -pub use self::error::*; - use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; use near_sdk::{ base64::engine::{general_purpose::STANDARD, Engine}, @@ -11,6 +7,10 @@ use near_sdk::{ near, AccountId, Promise, }; +pub use self::error::*; + +mod error; + #[ext_contract(ext_intent_contract)] pub trait IntentContract: FungibleTokenReceiver { /// Return pending intent by id. @@ -45,11 +45,11 @@ pub enum Action { } impl Action { - pub fn decode_base64(msg: impl AsRef<[u8]>) -> Result { + pub fn decode(msg: impl AsRef<[u8]>) -> Result { Self::try_from_slice(&STANDARD.decode(msg)?).map_err(|_| IntentError::Borsh) } - pub fn encode_base64(&self) -> Result { + pub fn encode(&self) -> Result { Ok(STANDARD.encode(borsh::to_vec(self).map_err(|_| IntentError::Borsh)?)) } } @@ -146,6 +146,7 @@ pub enum Expiration { Block(u64), } +/// The struct describes the token and amount of these tokens a user wants to exchange #[derive(Debug, Clone)] #[near(serializers=[borsh, json])] pub struct TokenAmount { @@ -189,25 +190,25 @@ fn test_create_action_serialize() { ); assert_eq!( - action.encode_base64().unwrap(), + action.encode().unwrap(), "AAEAAAAxCQAAAHVzZXIubmVhcgwAAAB0b2tlbl9hLm5lYXLoAwAAAAAAAAAAAAAAAAAADAAAAHRva2VuX2IubmVhctAHAAAAAAAAAAAAAAAAAAACQOIBAAAAAAABDQAAAHJlZmVycmFsLm5lYXI=" ); } #[test] fn test_create_action_deserialize() { - let action = Action::decode_base64("AAEAAAAxCQAAAHVzZXIubmVhcgwAAAB0b2tlbl9hLm5lYXLoAwAAAAAAAAAAAAAAAAAADAAAAHRva2VuX2IubmVhctAHAAAAAAAAAAAAAAAAAAACQOIBAAAAAAABDQAAAHJlZmVycmFsLm5lYXI=").unwrap(); + let action = Action::decode("AAEAAAAxCQAAAHVzZXIubmVhcgwAAAB0b2tlbl9hLm5lYXLoAwAAAAAAAAAAAAAAAAAADAAAAHRva2VuX2IubmVhctAHAAAAAAAAAAAAAAAAAAACQOIBAAAAAAABDQAAAHJlZmVycmFsLm5lYXI=").unwrap(); assert!(matches!(action, Action::CreateIntent(id, _) if id == "1")); } #[test] fn test_execute_action_serialize() { let action = Action::ExecuteIntent("1".to_string()); - assert_eq!(action.encode_base64().unwrap(), "AQEAAAAx"); + assert_eq!(action.encode().unwrap(), "AQEAAAAx"); } #[test] fn test_execute_action_deserialize() { - let action = Action::decode_base64("AQEAAAAx").unwrap(); + let action = Action::decode("AQEAAAAx").unwrap(); assert!(matches!(action, Action::ExecuteIntent(id) if id == "1")); } diff --git a/intent/src/lib.rs b/intent/src/lib.rs index a040fe4..abb9ecf 100644 --- a/intent/src/lib.rs +++ b/intent/src/lib.rs @@ -101,7 +101,7 @@ impl FungibleTokenReceiver for IntentContractImpl { ) -> PromiseOrValue { // Validate that sender_id is in white token list. // self.assert_token(&sender_id); // TODO: Check if we need tokens validation. - let action = Action::decode_base64(msg).expect("decode Action"); + let action = Action::decode(msg).expect("decode Action"); match action { Action::CreateIntent(id, intent) => { diff --git a/tests/src/tests/account/mod.rs b/tests/src/tests/account/mod.rs index 83bb45c..f90405a 100644 --- a/tests/src/tests/account/mod.rs +++ b/tests/src/tests/account/mod.rs @@ -1,6 +1,7 @@ -use crate::utils::Sandbox; use near_sdk::NearToken; +use crate::utils::Sandbox; + #[tokio::test] async fn test_deploy_contract() { let sandbox = Sandbox::new().await.unwrap(); diff --git a/tests/src/utils/intent.rs b/tests/src/utils/intent.rs index c252378..623d864 100644 --- a/tests/src/utils/intent.rs +++ b/tests/src/utils/intent.rs @@ -37,7 +37,7 @@ impl Intending for Account { ) { let amount = intent.send.amount; let intent = Action::CreateIntent(id.to_string(), intent); - let msg = intent.encode_base64().expect("encode Action"); + let msg = intent.encode().expect("encode Action"); let args = json!({ "receiver_id": intent_account_id, "amount": amount, @@ -64,7 +64,7 @@ impl Intending for Account { amount: U128, ) { let intent = Action::ExecuteIntent(id.to_string()); - let msg = intent.encode_base64().expect("encode Action"); + let msg = intent.encode().expect("encode Action"); let args = json!({ "receiver_id": intent_account_id, "amount": amount, From 2cfda100405e958fb5ab508e382cc3d7d0ff1ac6 Mon Sep 17 00:00:00 2001 From: De Fuse Date: Mon, 17 Jun 2024 20:54:14 +0400 Subject: [PATCH 7/8] chore: move AccountError to defuse-contracts --- account/src/account_db.rs | 35 ++++++++++++------- account/src/lib.rs | 2 -- .../src => contracts/src/account}/error.rs | 2 +- contracts/src/{account.rs => account/mod.rs} | 4 +++ 4 files changed, 27 insertions(+), 16 deletions(-) rename {account/src => contracts/src/account}/error.rs (91%) rename contracts/src/{account.rs => account/mod.rs} (95%) diff --git a/account/src/account_db.rs b/account/src/account_db.rs index 39bf4d2..597c0ca 100644 --- a/account/src/account_db.rs +++ b/account/src/account_db.rs @@ -1,10 +1,8 @@ use std::collections::HashMap; -use defuse_contracts::account::Account; +use defuse_contracts::account::{Account, AccountError}; use near_sdk::{near, store::LookupMap, AccountId, IntoStorageKey}; -use crate::Error; - // Accounts that belong user. Key here is derivation path. type UserAccounts = HashMap; @@ -21,10 +19,10 @@ impl AccountDb { account_id: AccountId, derivation_path: String, account: Account, - ) -> Result<(), Error> { + ) -> Result<(), AccountError> { if let Some(accounts) = self.0.get_mut(&account_id) { if accounts.contains_key(&derivation_path) { - return Err(Error::AccountExist); + return Err(AccountError::AccountExist); } accounts.insert(derivation_path, account); @@ -41,20 +39,27 @@ impl AccountDb { from: &AccountId, to: AccountId, derivation_path: String, - ) -> Result<(), Error> { + ) -> Result<(), AccountError> { let account = self .0 .get_mut(from) - .ok_or(Error::EmptyAccounts) - .and_then(|accounts| accounts.remove(&derivation_path).ok_or(Error::NoAccount))?; + .ok_or(AccountError::EmptyAccounts) + .and_then(|accounts| { + accounts + .remove(&derivation_path) + .ok_or(AccountError::NoAccount) + })?; self.add_account(to, derivation_path, account) } - pub fn get_accounts(&self, account_id: &AccountId) -> Result, Error> { + pub fn get_accounts( + &self, + account_id: &AccountId, + ) -> Result, AccountError> { self.0 .get(account_id) - .map_or(Err(Error::EmptyAccounts), |accounts| { + .map_or(Err(AccountError::EmptyAccounts), |accounts| { Ok(accounts .iter() .map(|(d, a)| (d.clone(), a.clone())) @@ -63,7 +68,11 @@ impl AccountDb { } #[allow(dead_code)] - pub fn assert_owner(&self, account_id: &AccountId, derivation_path: &str) -> Result<(), Error> { + pub fn assert_owner( + &self, + account_id: &AccountId, + derivation_path: &str, + ) -> Result<(), AccountError> { self.0 .get(account_id) .and_then(|accounts| { @@ -73,7 +82,7 @@ impl AccountDb { None } }) - .ok_or(Error::EmptyAccounts) + .ok_or(AccountError::EmptyAccounts) } } @@ -108,7 +117,7 @@ fn test_account_db_change_owner() { assert!(matches!( db.assert_owner(&account_id, &path), - Err(Error::EmptyAccounts) + Err(AccountError::EmptyAccounts) )); assert!(matches!(db.assert_owner(&new_owner, &path), Ok(()))); } diff --git a/account/src/lib.rs b/account/src/lib.rs index eb154d9..efc2e8a 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -2,10 +2,8 @@ use defuse_contracts::account::{Account, AccountContract}; use near_sdk::{env, near, store::LookupSet, AccountId, BorshStorageKey, PanicOnDefault}; use self::account_db::AccountDb; -pub use self::error::*; mod account_db; -mod error; #[derive(BorshStorageKey)] #[near(serializers=[borsh])] diff --git a/account/src/error.rs b/contracts/src/account/error.rs similarity index 91% rename from account/src/error.rs rename to contracts/src/account/error.rs index b373151..51e8d67 100644 --- a/account/src/error.rs +++ b/contracts/src/account/error.rs @@ -1,7 +1,7 @@ use thiserror::Error as ThisError; #[derive(Debug, ThisError)] -pub enum Error { +pub enum AccountError { #[error("account doesn't exist")] AccountExist, #[error("user doesn't have any accounts")] diff --git a/contracts/src/account.rs b/contracts/src/account/mod.rs similarity index 95% rename from contracts/src/account.rs rename to contracts/src/account/mod.rs index 3cc612a..7b7993b 100644 --- a/contracts/src/account.rs +++ b/contracts/src/account/mod.rs @@ -1,5 +1,9 @@ use near_sdk::{ext_contract, near, AccountId}; +pub use self::error::AccountError; + +mod error; + // TODO: make this contract an extension of NFT, since every Account // is a unique item. This will ease the integration of Defuse // with already existing tooling around NFTs From ac7a958d440e3d88e45ae71a3d72f29e314b7597 Mon Sep 17 00:00:00 2001 From: De Fuse Date: Mon, 17 Jun 2024 21:02:12 +0400 Subject: [PATCH 8/8] chore: add lints.workspace = true to all crates --- contracts/Cargo.toml | 3 +++ contracts/src/intent/mod.rs | 11 +++++++++++ controller/Cargo.toml | 3 +++ 3 files changed, 17 insertions(+) diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 2c111c0..575e002 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -3,6 +3,9 @@ name = "defuse-contracts" version = "0.1.0" edition.workspace = true +[lints] +workspace = true + [dependencies] near-sdk.workspace = true near-contract-standards.workspace = true diff --git a/contracts/src/intent/mod.rs b/contracts/src/intent/mod.rs index 2fb6e3c..011fe4e 100644 --- a/contracts/src/intent/mod.rs +++ b/contracts/src/intent/mod.rs @@ -45,10 +45,21 @@ pub enum Action { } impl Action { + /// Decode provided msg into `Action`. + /// + /// # Errors + /// + /// `IntentError::Base64` + /// `IntentError::Borsh` pub fn decode(msg: impl AsRef<[u8]>) -> Result { Self::try_from_slice(&STANDARD.decode(msg)?).map_err(|_| IntentError::Borsh) } + /// Encode the action into a string message. + /// + /// # Errors + /// + /// `IntentError::Borch` pub fn encode(&self) -> Result { Ok(STANDARD.encode(borsh::to_vec(self).map_err(|_| IntentError::Borsh)?)) } diff --git a/controller/Cargo.toml b/controller/Cargo.toml index f09c9c2..0e9111b 100644 --- a/controller/Cargo.toml +++ b/controller/Cargo.toml @@ -6,6 +6,9 @@ edition.workspace = true [lib] crate-type = ["cdylib", "rlib"] +[lints] +workspace = true + [dependencies] defuse-contracts = { workspace = true, features = ["account", "controller"] }