Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
142726e
pubkey account synchronizer added
hlgltvnnk Jan 7, 2026
475e5a7
test fixed
hlgltvnnk Jan 8, 2026
00dc35b
mint tokens method added
hlgltvnnk Jan 8, 2026
8f56a19
mint tokens fn call added
hlgltvnnk Jan 8, 2026
47cb36c
burn intent added
hlgltvnnk Jan 8, 2026
552904f
burn intent added
hlgltvnnk Jan 9, 2026
b940a6f
naming upd
hlgltvnnk Jan 12, 2026
60524fd
mint + burn intents changes
hlgltvnnk Jan 12, 2026
1e277ae
tests updated
hlgltvnnk Jan 12, 2026
3347af8
minor edits
hlgltvnnk Jan 12, 2026
fdf1353
mint intent updated
hlgltvnnk Jan 13, 2026
6ba4a39
fix
hlgltvnnk Jan 13, 2026
e5c3096
fix
hlgltvnnk Jan 13, 2026
3e6ca0b
review changes
hlgltvnnk Jan 13, 2026
a82384f
fix
hlgltvnnk Jan 13, 2026
99e38ee
burn intent removed
hlgltvnnk Jan 15, 2026
8146632
notification added to mt_mint intent
hlgltvnnk Jan 15, 2026
974ab4f
dip5 standart added
hlgltvnnk Jan 16, 2026
6602cee
burn intents added + tests updated
hlgltvnnk Jan 16, 2026
2aa54eb
burn tests added
hlgltvnnk Jan 16, 2026
174654e
typo fix
hlgltvnnk Jan 16, 2026
416ad99
naming updated
hlgltvnnk Jan 16, 2026
18f4268
simulate burn intent test added
hlgltvnnk Jan 16, 2026
b2b2e13
refactoring
hlgltvnnk Jan 22, 2026
1f0037c
Merge branch 'main' into feat/far-acc-management
hlgltvnnk Jan 22, 2026
a1e0f37
imt feature added
hlgltvnnk Jan 22, 2026
e5f8b68
notification min gas upd
hlgltvnnk Jan 23, 2026
38344f4
mint intent updated
hlgltvnnk Jan 23, 2026
a352ecd
minor edit
hlgltvnnk Jan 23, 2026
709aeac
refactoring
hlgltvnnk Jan 23, 2026
deeace5
minter added to burn intent + notification static gas limits updated
hlgltvnnk Jan 26, 2026
6cac2b2
doc fix
hlgltvnnk Jan 26, 2026
d75da27
fix
hlgltvnnk Jan 26, 2026
37a1953
fix
hlgltvnnk Jan 26, 2026
bf617a7
refactorign
hlgltvnnk Jan 26, 2026
ea03a31
fix
hlgltvnnk Jan 26, 2026
60c1c4b
fix
hlgltvnnk Jan 26, 2026
6a3b585
fix
hlgltvnnk Jan 26, 2026
9cc44df
chore
mitinarseny Jan 26, 2026
f54ae8b
fix: allocate more gas for implicit Near account_id transfer
mitinarseny Jan 26, 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
20 changes: 20 additions & 0 deletions core/src/engine/state/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,26 @@ where

Ok(())
}

#[inline]
fn mt_mint(
&mut self,
owner_id: AccountId,
tokens: Amounts,
_memo: Option<String>,
) -> Result<()> {
self.internal_add_balance(owner_id, tokens)
}

#[inline]
fn mt_burn(
&mut self,
owner_id: &AccountIdRef,
tokens: Amounts,
_memo: Option<String>,
) -> Result<()> {
self.internal_sub_balance(owner_id, tokens)
}
}

#[derive(Debug, Default)]
Expand Down
20 changes: 20 additions & 0 deletions core/src/engine/state/deltas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,26 @@ where
fn auth_call(&mut self, signer_id: &AccountIdRef, auth_call: AuthCall) -> Result<()> {
self.state.auth_call(signer_id, auth_call)
}

#[inline]
fn mt_mint(
&mut self,
owner_id: AccountId,
tokens: Amounts,
memo: Option<String>,
) -> Result<()> {
self.state.mt_mint(owner_id, tokens, memo)
}

#[inline]
fn mt_burn(
&mut self,
owner_id: &AccountIdRef,
tokens: Amounts,
memo: Option<String>,
) -> Result<()> {
self.state.mt_burn(owner_id, tokens, memo)
}
}

/// Accumulates internal deposits and withdrawals on different tokens
Expand Down
10 changes: 10 additions & 0 deletions core/src/engine/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,14 @@ pub trait State: StateView {
fn set_auth_by_predecessor_id(&mut self, account_id: AccountId, enable: bool) -> Result<bool>;

fn auth_call(&mut self, signer_id: &AccountIdRef, auth_call: AuthCall) -> Result<()>;

fn mt_mint(&mut self, owner_id: AccountId, tokens: Amounts, memo: Option<String>)
-> Result<()>;

fn mt_burn(
&mut self,
owner_id: &AccountIdRef,
tokens: Amounts,
memo: Option<String>,
) -> Result<()>;
}
10 changes: 9 additions & 1 deletion core/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::{
IntentEvent,
account::SetAuthByPredecessorId,
token_diff::TokenDiffEvent,
tokens::{FtWithdraw, MtWithdraw, NativeWithdraw, NftWithdraw, StorageDeposit},
tokens::{
FtWithdraw, MtBurn, MtMint, MtWithdraw, NativeWithdraw, NftWithdraw, StorageDeposit,
},
},
};

Expand Down Expand Up @@ -54,6 +56,12 @@ pub enum DefuseEvent<'a> {
#[event_version("0.3.0")]
StorageDeposit(Cow<'a, [IntentEvent<AccountEvent<'a, Cow<'a, StorageDeposit>>>]>),

#[event_version("0.3.0")]
MtMint(Cow<'a, [IntentEvent<AccountEvent<'a, Cow<'a, MtMint>>>]>),

#[event_version("0.3.0")]
MtBurn(Cow<'a, [IntentEvent<AccountEvent<'a, Cow<'a, MtBurn>>>]>),

#[event_version("0.3.0")]
#[from(skip)]
AccountLocked(AccountEvent<'a, ()>),
Expand Down
14 changes: 13 additions & 1 deletion core/src/intents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ use tokens::{NativeWithdraw, StorageDeposit};
use crate::{
Result,
engine::{Engine, Inspector, State},
intents::{account::SetAuthByPredecessorId, auth::AuthCall},
intents::{
account::SetAuthByPredecessorId,
auth::AuthCall,
tokens::{MtBurn, MtMint},
},
};

use self::{
Expand Down Expand Up @@ -68,6 +72,12 @@ pub enum Intent {

/// See [`AuthCall`]
AuthCall(AuthCall),

// See [`MtMint`]
MtMint(MtMint),

// See [`MtBurn`]
MtBurn(MtBurn),
}

pub trait ExecutableIntent {
Expand Down Expand Up @@ -125,6 +135,8 @@ impl ExecutableIntent for Intent {
intent.execute_intent(signer_id, engine, intent_hash)
}
Self::AuthCall(intent) => intent.execute_intent(signer_id, engine, intent_hash),
Self::MtMint(intent) => intent.execute_intent(signer_id, engine, intent_hash),
Self::MtBurn(intent) => intent.execute_intent(signer_id, engine, intent_hash),
}
}
}
Expand Down
93 changes: 93 additions & 0 deletions core/src/intents/tokens.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{borrow::Cow, collections::BTreeMap};

use defuse_token_id::nep245::Nep245TokenId;
use near_contract_standards::non_fungible_token;
use near_sdk::{
AccountId, AccountIdRef, CryptoHash, Gas, NearToken, json_types::U128, near,
Expand Down Expand Up @@ -516,3 +517,95 @@ impl ExecutableIntent for StorageDeposit {
engine.state.storage_deposit(owner_id, self)
}
}

#[near(serializers = [borsh, json])]
#[derive(Debug, Clone)]
/// Mint a set of tokens from the signer to a specified account id, within the intents contract.
pub struct MtMint {
pub receiver_id: AccountId,

#[serde_as(as = "Amounts<BTreeMap<_, DisplayFromStr>>")]
pub tokens: Amounts,

#[serde(default, skip_serializing_if = "Option::is_none")]
pub memo: Option<String>,
}

impl ExecutableIntent for MtMint {
#[inline]
fn execute_intent<S, I>(
self,
owner_id: &AccountIdRef,
engine: &mut Engine<S, I>,
intent_hash: CryptoHash,
) -> Result<()>
where
S: State,
I: Inspector,
{
engine.inspector.on_event(DefuseEvent::MtMint(Cow::Borrowed(
[IntentEvent::new(
AccountEvent::new(owner_id, Cow::Borrowed(&self)),
intent_hash,
)]
.as_slice(),
)));

let tokens = Amounts::new(
self.tokens
.iter()
.map(|(token_id, amount)| {
let token = Nep245TokenId::new(owner_id, token_id.to_string());
(token.into(), *amount)
})
.collect::<BTreeMap<_, _>>(),
);

engine.state.mt_mint(self.receiver_id, tokens, self.memo)
}
}

#[near(serializers = [borsh, json])]
#[derive(Debug, Clone)]
/// Burn a set of tokens minted by signer, within the intents contract.
pub struct MtBurn {
#[serde_as(as = "Amounts<BTreeMap<_, DisplayFromStr>>")]
pub tokens: Amounts,

#[serde(default, skip_serializing_if = "Option::is_none")]
pub memo: Option<String>,
}

impl ExecutableIntent for MtBurn {
#[inline]
fn execute_intent<S, I>(
self,
owner_id: &AccountIdRef,
engine: &mut Engine<S, I>,
intent_hash: CryptoHash,
) -> Result<()>
where
S: State,
I: Inspector,
{
engine.inspector.on_event(DefuseEvent::MtBurn(Cow::Borrowed(
[IntentEvent::new(
AccountEvent::new(owner_id, Cow::Borrowed(&self)),
intent_hash,
)]
.as_slice(),
)));

let tokens = Amounts::new(
self.tokens
.iter()
.map(|(token_id, amount)| {
let token = Nep245TokenId::new(owner_id, token_id.to_string());
(token.into(), *amount)
})
.collect::<BTreeMap<_, _>>(),
);

engine.state.mt_burn(owner_id, tokens, self.memo)
}
}
17 changes: 17 additions & 0 deletions defuse/src/account_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use defuse_core::crypto::PublicKey;
use near_sdk::{AccountId, ext_contract};

// TODO: locked accounts + accounts with predecessor id disabled should be also synchronized
#[ext_contract(ext_pk_sync_manager)]
pub trait AccountSyncManager {
/// Registers or re-activates `public_key` under the user account_id.
///
/// NOTE: MUST attach 1 yⓃ for security purposes.
fn force_add_public_keys(&mut self, public_keys: Vec<(AccountId, Vec<PublicKey>)>);

/// Deactivate `public_key` from the user account_id,
/// i.e. this key can't be used to make any actions unless it's re-created.
///
/// NOTE: MUST attach 1 yⓃ for security purposes.
fn force_remove_public_keys(&mut self, public_keys: Vec<(AccountId, Vec<PublicKey>)>);
}
61 changes: 61 additions & 0 deletions defuse/src/contract/account_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use std::borrow::Cow;

use defuse_core::{
accounts::{AccountEvent, PublicKeyEvent},
crypto::PublicKey,
engine::State,
events::DefuseEvent,
};

use defuse_near_utils::UnwrapOrPanic;

use near_plugins::{AccessControllable, access_control_any};
use near_sdk::{AccountId, assert_one_yocto, near};

use crate::{
account_sync::AccountSyncManager,
contract::{Contract, ContractExt, Role},
};

#[near]
impl AccountSyncManager for Contract {
#[access_control_any(roles(Role::DAO, Role::PubKeySynchronizer))]
#[payable]
fn force_add_public_keys(&mut self, entries: Vec<(AccountId, Vec<PublicKey>)>) {
assert_one_yocto();

for (account_id, keys) in entries {
for public_key in keys {
State::add_public_key(self, account_id.clone(), public_key).unwrap_or_panic();

DefuseEvent::PublicKeyAdded(AccountEvent::new(
Cow::Borrowed(account_id.as_ref()),
PublicKeyEvent {
public_key: Cow::Borrowed(&public_key),
},
))
.emit();
}
}
}

#[access_control_any(roles(Role::DAO, Role::PubKeySynchronizer))]
#[payable]
fn force_remove_public_keys(&mut self, entries: Vec<(AccountId, Vec<PublicKey>)>) {
assert_one_yocto();

for (account_id, keys) in entries {
for public_key in keys {
State::remove_public_key(self, account_id.clone(), public_key).unwrap_or_panic();

DefuseEvent::PublicKeyRemoved(AccountEvent::new(
Cow::Borrowed(account_id.as_ref()),
PublicKeyEvent {
public_key: Cow::Borrowed(&public_key),
},
))
.emit();
}
}
}
}
20 changes: 20 additions & 0 deletions defuse/src/contract/intents/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,24 @@ impl State for Contract {

Ok(())
}

#[inline]
fn mt_mint(
&mut self,
owner_id: AccountId,
tokens: Amounts,
memo: Option<String>,
) -> Result<()> {
self.deposit(owner_id, tokens, memo.as_deref())
}

#[inline]
fn mt_burn(
&mut self,
owner_id: &AccountIdRef,
tokens: Amounts,
memo: Option<String>,
) -> Result<()> {
self.withdraw(owner_id, tokens, memo, false)
}
}
3 changes: 3 additions & 0 deletions defuse/src/contract/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(all(feature = "abi", not(target_arch = "wasm32")))]
mod abi;
mod account_sync;
mod accounts;
mod admin;
pub mod config;
Expand Down Expand Up @@ -53,6 +54,8 @@ pub enum Role {
SaltManager,

GarbageCollector,

PubKeySynchronizer,
}

#[access_control(role_type(Role))]
Expand Down
2 changes: 2 additions & 0 deletions defuse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub mod salts;
pub mod simulation_output;
pub mod tokens;

pub mod account_sync;

pub use defuse_core as core;
pub use defuse_nep245 as nep245;

Expand Down
Loading
Loading