Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1c3f812
wip
pityjllk Mar 17, 2026
1d4a95b
fix compilation warnings for different features combinations
pityjllk Mar 17, 2026
44a050f
cleanup makefile
pityjllk Mar 17, 2026
5511d95
simplify imports
pityjllk Mar 17, 2026
a12ef5d
Merge remote-tracking branch 'origin/main' into feat/cargo-check-feat…
pityjllk Mar 17, 2026
fcc452c
fix clippy
pityjllk Mar 17, 2026
4fc64d1
use wasm compiler for clippy
pityjllk Mar 17, 2026
5638b48
address comment
pityjllk Mar 17, 2026
0a02ec5
use cargo clippy with cargo hack
pityjllk Mar 17, 2026
9fc3ee1
simplify
pityjllk Mar 17, 2026
b11ae64
fix formatting
pityjllk Mar 17, 2026
6e13692
improve makefile
pityjllk Mar 19, 2026
7a98695
clenup makefile
pityjllk Mar 20, 2026
426de9f
prepare for merge
pityjllk Mar 24, 2026
4e9530e
Merge remote-tracking branch 'origin/main' into feat/cargo-check-feat…
pityjllk Mar 24, 2026
cab4e5b
remove --no-dev-deps
pityjllk Mar 24, 2026
80741fa
cleanup makefile
pityjllk Mar 24, 2026
3e88230
fix clippy warnings
pityjllk Mar 24, 2026
f2d96ff
simplify help
pityjllk Mar 24, 2026
26c2810
Merge remote-tracking branch 'origin/main' into feat/cargo-check-feat…
pityjllk Mar 24, 2026
81487e9
reorder targets
pityjllk Mar 25, 2026
e23f5f8
cleanup makefile
pityjllk Mar 25, 2026
e5100e0
improve error
pityjllk Mar 25, 2026
bba177b
Merge remote-tracking branch 'origin/main' into feat/cargo-check-feat…
pityjllk Apr 14, 2026
1255ed7
use single step for all checks
pityjllk Apr 14, 2026
a0ab7d4
exclude defuse-wallet-sdk from wasm clippy check
pityjllk Apr 14, 2026
65049c1
Revert "use single step for all checks"
pityjllk Apr 14, 2026
7836430
Merge remote-tracking branch 'origin/main' into feat/cargo-check-feat…
pityjllk Apr 15, 2026
ca04e05
improve cargo check
pityjllk Apr 15, 2026
f4379ec
address comments
pityjllk Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@ jobs:
echo 'Signature: 8a477f597d28d172789f06886806bc55' | tee target/CACHEDIR.TAG res/CACHEDIR.TAG > /dev/null
- name: Run clippy for workspace
run: make check
- name: Run clippy for defuse
# Run clippy only on the main contract feature
run: cargo clippy --manifest-path ./contracts/defuse/Cargo.toml --features=contract --features=near-sdk/non-contract-usage --no-deps
- name: Install cargo-machete
run: curl -fsSL https://github.com/bnjbvr/cargo-machete/releases/download/v0.9.1/cargo-machete-v0.9.1-x86_64-unknown-linux-musl.tar.gz | tar xz --strip-components=1 -C /usr/local/bin
- name: Check all examples
run: make check-examples
- name: Check for unused dependencies
run: make check-unused-deps
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: Check all feature combinations
run: make check-all-features

build:
name: Build
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ near-sdk = "5.26.1"
near-workspaces = "0.22"

anyhow = "1"

arbitrary = "1"
# TODO: `features = ["derive"]` needed because near-sdk uses `derive(Arbitrary)`
# but doesn't enable the feature itself. Remove once fixed upstream in near-sdk.
arbitrary = { version = "1", features = ["derive"] }
array-util = "1"
bitflags = "2.9.1"
bnum = { version = "0.13", features = ["borsh"] }
Expand Down
160 changes: 116 additions & 44 deletions Makefile
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please move all the crappy stuff to the end of the file, while keeping minimalistic and understandable targets on top?

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,97 @@ DEFUSE_OUT_DIR ?= $(ROOT_DIR)res
MAKE_OUT_DIR_PREFIX ?= $(ROOT_DIR)target/makenear
MAKE_OUT_DIR = $(eval MAKE_OUT_DIR := $(shell mkdir -p $(MAKE_OUT_DIR_PREFIX) $(DEFUSE_OUT_DIR) && mktemp -d -p $(MAKE_OUT_DIR_PREFIX)))$(MAKE_OUT_DIR)

.PHONY: help
help:
@echo "Usage: make [target] [REPRODUCIBLE=1]"
@echo ""
@echo "Build targets (use REPRODUCIBLE=1 for reproducible builds):"
@$(foreach t,$(ALL_TARGETS),echo " $(t)";)
@echo ""
@echo "Other targets:"
@echo " all Build all contracts (default)"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're touching this already and we're going to have outlayer contracts and services, maybe it makes sense to introduce make contracts target and make it a pre-requizite of make all?

@echo " clean Remove build artifacts and cargo clean"
@echo " test Run all workspace tests"
@echo " check Run clippy on codebase (codebase + per-contract wasm)"
@echo " check-all Run all checks"
@echo " fmt Format Rust files and Cargo.toml manifests"
@echo " help Show this help"

.PHONY: clean-out-dir
clean-out-dir:
rm -rf $(DEFUSE_OUT_DIR)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side note: what if $(DEFUSE_OUT_DIR) does not exist?


.PHONY: clean
clean: clean-out-dir
cargo clean

.PHONY: test
test:
cargo test --workspace --all-targets

.PHONY: check
check: check-contracts
cargo clippy --workspace --all-targets --no-deps

.PHONY: check-contracts

.PHONY: check-fmt
check-fmt:
cargo fmt --all --check
RUST_LOG=warn taplo format --check

.PHONY: check-unused-deps
check-unused-deps:
cargo machete 2>/dev/null

.PHONY: check-examples
check-examples:
RUSTFLAGS='$(RUSTFLAGS_CHECK)' cargo clippy --workspace --examples

.PHONY: check-all
check-all: check-fmt check-unused-deps check check-examples check-all-features

.PHONY: fmt
fmt:
cargo fmt --all
taplo format

RUSTFLAGS_CHECK = -D warnings
# --cfg clippy: cargo clippy only sets cfg(clippy) for workspace crates, not dependencies.
# near-sdk compile_error!s on host unless one of its allowed cfgs is set, so we must set it via RUSTFLAGS.
# --include-features near-sdk/non-contract-usage would be cleaner, but cargo-hack's
# --ignore-unknown-features is not implemented for --include-features, so it fails
# on crates that don't depend on near-sdk.
CARGO_CHECK_HOST = RUSTFLAGS='$(RUSTFLAGS_CHECK) --cfg clippy' cargo hack clippy --exclude-features contract
CARGO_CHECK_WASM = RUSTFLAGS='$(RUSTFLAGS_CHECK)' cargo hack clippy --target wasm32-unknown-unknown --exclude-features abi --exclude-features near-api-types --exclude-features near-api --no-dev-deps

# Crates where every enum variant is feature-gated, requiring at least one
# variant feature. These need --feature-powerset + --at-least-one-of instead
# of --each-feature (which tests features in isolation).
# Format: crate=feature1,feature2,...
CRATES_AT_LEAST_ONE_VARIANT := \
defuse-token-id=nep141,nep171,nep245,imt \
defuse-ton-connect=text,binary,cell \
defuse-escrow-swap=nep141,nep245

# Testing crates that cannot compile for wasm32-unknown-unknown.
# defuse-randomness uses rand/getrandom which lacks wasm32 support;
# it only reaches the defuse contract via dev-dependencies, never in the WASM binary.
# defuse-wallet-sdk uses getrandom (via rand) without a wasm backend feature.
CRATES_HOST_ONLY := \
defuse-test-utils \
defuse-sandbox \
defuse-randomness \
defuse-tests \
defuse-wallet-relayer \
defuse-wallet-sdk

# Crates excluded from check-all-features-host (still covered by `make check`).
# defuse-wallet-relayer: aws-lc-sys build script breaks with --cfg clippy in RUSTFLAGS.
CRATES_SKIP_HOST_FEATURES := \
defuse-wallet-relayer


.DEFAULT_GOAL := all
CONTRACT_CRATES := \
defuse \
Expand All @@ -16,6 +107,9 @@ CONTRACT_CRATES := \

ALL_TARGETS :=

crate_name = $(firstword $(subst =, ,$1))
crate_features = $(lastword $(subst =, ,$1))

# Generate all build targets from cargo metadata, filtered to CONTRACT_CRATES
$(eval $(shell cargo metadata --format-version=1 | jq -rn \
--arg outdir '$(DEFUSE_OUT_DIR)' --arg reproducible '$(REPRODUCIBLE)' --arg makedir '$$$$(MAKE_OUT_DIR)' \
Expand All @@ -36,6 +130,10 @@ $(eval $(shell cargo metadata --format-version=1 | jq -rn \
($$vval.container_build_command | join(" ")) as $$non_reproducible_cmd | \
(if $$reproducible != "" then $$reproducible_cmd else $$non_reproducible_cmd end) as $$cmd | \
(if $$vkey == "" then "" else ".\($$vkey)" end) as $$suffix | \
($$vval.container_build_command | map(select(startswith("--features="))) | if length > 0 then " " + first else "" end) as $$features_flag | \
"$$(eval .PHONY: check-contract/\($$tname))", \
"$$(eval check-contracts:: check-contract/\($$tname))", \
"$$(eval check-contract/\($$tname):; cargo clippy -p \($$name) --no-deps --target wasm32-unknown-unknown\($$features_flag))", \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add check-contract/<CONTRACT>/all?

"$$(eval .PHONY: \($$tname))", \
"$$(eval ALL_TARGETS += \($$tname))", \
"$$(eval \($$name)/all:: \($$tname))", \
Expand All @@ -47,51 +145,25 @@ $(eval $(shell cargo metadata --format-version=1 | jq -rn \
.PHONY: all
all: $(ALL_TARGETS)

.PHONY: help
help:
@echo "Usage: make [target] [REPRODUCIBLE=1]"
@echo ""
@echo "Build targets (use REPRODUCIBLE=1 for reproducible builds):"
@$(foreach t,$(ALL_TARGETS),echo " $(t)";)
@echo ""
@echo "Other targets:"
@echo " all Build all contracts (default)"
@echo " clean Remove build artifacts and cargo clean"
@echo " clean-out-dir Remove output directory only"
@echo " test Run all workspace tests"
@echo " clippy Run clippy lints"
@echo " fmt Format Rust files and Cargo.toml manifests"
@echo " help Show this help"
.PHONY: check-all-features-host
check-all-features-host::
$(CARGO_CHECK_HOST) --workspace --each-feature --exclude-no-default-features \
$(foreach c,$(CRATES_AT_LEAST_ONE_VARIANT),--exclude $(call crate_name,$c)) \
$(addprefix --exclude ,$(CRATES_SKIP_HOST_FEATURES))

.PHONY: clean-out-dir
clean-out-dir:
rm -rf $(DEFUSE_OUT_DIR)
$(foreach c,$(CRATES_AT_LEAST_ONE_VARIANT),\
$(eval check-all-features-host::; \
$(CARGO_CHECK_HOST) -p $(call crate_name,$c) --feature-powerset --at-least-one-of $(call crate_features,$c)))

.PHONY: clean
clean: clean-out-dir
cargo clean
.PHONY: check-all-features-wasm
check-all-features-wasm::
$(CARGO_CHECK_WASM) --workspace --each-feature --exclude-no-default-features \
$(foreach c,$(CRATES_AT_LEAST_ONE_VARIANT),--exclude $(call crate_name,$c)) \
$(addprefix --exclude ,$(CRATES_HOST_ONLY))

.PHONY: test
test:
cargo test --workspace --all-targets

.PHONY: check
check:
cargo clippy --workspace --all-targets --no-deps

.PHONY: check-fmt
check-fmt:
cargo fmt --all --check
RUST_LOG=warn taplo format --check
$(foreach c,$(CRATES_AT_LEAST_ONE_VARIANT),\
$(eval check-all-features-wasm::; \
$(CARGO_CHECK_WASM) -p $(call crate_name,$c) --feature-powerset --at-least-one-of $(call crate_features,$c)))

Comment on lines +158 to 167
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, this PR tries to check every crate we have for WASM target with all supported feature combinations.
This is, of course, a very comprehensive approach. But it seems to me that we're trying to cover all target/feature combinations but then I'm also asking myself: why do we check only host and WASM targets? Why don't we check different host targets as well instead of just the current host target?
Reason for WASM is clear: we use it in contracts. But, if so, then why do we need these additional per-crate checks, if the effectively needed result is already achieved by check-contracts make target (I like that!), which is auto-generated from reproducible builds metadata in contracts Cargo.toml?

The reason for me worrying about this is that we will have to maintain these CRATES_AT_LEAST_ONE_VARIANT, CRATES_SKIP_HOST_FEATURES, CRATES_HOST_ONLY and etc in this Makefile, which would only grow in size more and more in the future and it will be a pain to maintain it...

What do you think if we only keep make check-contracts and avoid using cargo hack entirely?🫠

.PHONY: check-unused-deps
check-unused-deps:
cargo machete 2>/dev/null

.PHONY: check-all
check-all: check-fmt check check-unused-deps

.PHONY: fmt
fmt:
cargo fmt --all
taplo format
.PHONY: check-all-features
check-all-features: check-all-features-host check-all-features-wasm
3 changes: 1 addition & 2 deletions contracts/defuse/core/src/nonce/salted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ const _: () = {
fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
extensions: [("contentEncoding", "hex".into())]
.into_iter()
extensions: std::iter::once(("contentEncoding", "hex".into()))
.map(|(k, v)| (k.to_string(), v))
.collect(),
..Default::default()
Expand Down
2 changes: 1 addition & 1 deletion contracts/defuse/core/src/payload/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<T> ExtractDefusePayload<T> for DefusePayload<T> {

#[cfg(feature = "abi")]
mod examples {
use super::*;
use super::Nonce;

use near_sdk::base64::{self, Engine};

Expand Down
10 changes: 5 additions & 5 deletions contracts/escrow-swap/src/contract/auth_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ use super::{Contract, ContractExt};
#[near]
impl AuthCallee for Contract {
fn on_auth(&mut self, signer_id: AccountId, msg: String) -> PromiseOrValue<()> {
self.internal_on_auth(&signer_id, msg).unwrap_or_panic()
self.internal_on_auth(&signer_id, &msg).unwrap_or_panic()
}
}

impl Contract {
fn internal_on_auth(
&mut self,
signer_id: &AccountIdRef,
msg: String,
msg: &str,
) -> Result<PromiseOrValue<()>> {
let msg: Message = serde_json::from_str(&msg)?;
let msg: Message = serde_json::from_str(msg)?;

let mut guard = self.cleanup_guard();
let state = guard.try_as_alive_mut()?.verify_mut(&msg.params)?;

if !msg
if msg
.params
.auth_caller
.as_ref()
.is_some_and(|a| *a == env::predecessor_account_id())
.is_none_or(|a| *a != env::predecessor_account_id())
{
return Err(Error::Unauthorized);
}
Expand Down
10 changes: 6 additions & 4 deletions contracts/escrow-swap/src/contract/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ use super::{Contract, ContractExt};

impl Contract {
#[inline]
pub(super) fn cleanup_guard(&mut self) -> CleanupGuard<'_> {
pub(super) const fn cleanup_guard(&mut self) -> CleanupGuard<'_> {
CleanupGuard(&mut self.0)
}
}

pub struct CleanupGuard<'a>(&'a mut ContractStorage);

impl<'a> CleanupGuard<'a> {
impl CleanupGuard<'_> {
#[inline]
pub const fn as_alive_mut(&mut self) -> Option<&mut Storage> {
self.0.0.as_mut()
Expand Down Expand Up @@ -56,9 +56,11 @@ impl<'a> CleanupGuard<'a> {
}
}

impl<'a> Drop for CleanupGuard<'a> {
impl Drop for CleanupGuard<'_> {
fn drop(&mut self) {
self.maybe_cleanup().map(Promise::detach);
if let Some(p) = self.maybe_cleanup() {
p.detach();
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/escrow-swap/src/contract/close.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ impl State {
self.close_unchecked(reason);
}

self.lost_found(params)
Ok(self.lost_found(params))
}
}
6 changes: 3 additions & 3 deletions contracts/escrow-swap/src/contract/fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,13 @@ impl ProtocolFees {
) -> Result<ProtocolFeesCollected<'static>> {
Ok(ProtocolFeesCollected {
fee: self.fee.fee_ceil(taker_dst_used),
surplus: if !self.surplus.is_zero() {
surplus: if self.surplus.is_zero() {
0
} else {
let maker_want_dst = <u128 as CheckedMul<UD128>>::checked_mul(src_out, maker_price)
.ok_or(Error::IntegerOverflow)?;
let surplus = taker_dst_used.saturating_sub(maker_want_dst);
self.surplus.fee_ceil(surplus)
} else {
0
},
collector: self.collector.into(),
})
Expand Down
4 changes: 2 additions & 2 deletions contracts/escrow-swap/src/contract/fund.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ impl State {
pub(super) fn fund(
&mut self,
params: Params,
sender_id: AccountId,
sender_id: &AccountId,
amount: u128,
) -> Result<PromiseOrValue<u128>> {
if sender_id != params.maker {
if *sender_id != params.maker {
return Err(Error::Unauthorized);
}

Expand Down
15 changes: 5 additions & 10 deletions contracts/escrow-swap/src/contract/lost_found.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl Contract {
Ok(guard
.try_as_alive_mut()?
.verify_mut(&params)?
.lost_found(params)?
.lost_found(params)
.map_or_else(
|| PromiseOrValue::Value(guard.maybe_cleanup().is_some()),
PromiseOrValue::Promise,
Expand All @@ -25,7 +25,7 @@ impl Contract {
}

impl State {
pub(super) fn lost_found(&mut self, params: Params) -> Result<Option<Promise>> {
pub(super) fn lost_found(&mut self, params: Params) -> Option<Promise> {
let (sent_src, send_src_p) = self
.closed
.then(|| mem::take(&mut self.maker_src_remaining))
Expand Down Expand Up @@ -59,18 +59,13 @@ impl State {
})
.unzip();

let Some(send) = send_src_p
let send = send_src_p
.into_iter()
.chain(send_dst_p)
.reduce(Promise::and)
else {
return Ok(None);
};
.reduce(Promise::and)?;

Event::MakerRefunded(MakerSent::from_sent(sent_src, sent_dst)).emit();

Ok(send
.then(self.callback_resolve_transfers(sent_src, sent_dst))
.into())
Some(send.then(self.callback_resolve_transfers(sent_src, sent_dst)))
}
}
Loading
Loading