Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5de301f
feat(iota-e2e-tests): new test_receiving_gas_in_two_separate_txs test…
Dkwcs Jan 19, 2026
29a8629
ObjectVersionUnavailableForConsumption panic case in the test
Dkwcs Jan 19, 2026
a7081c9
refactor(e2e): aa receiving test after rebase
miker83z Jan 20, 2026
5b37980
fix(core): tx input loader rejects receiving objects owned by AA acco…
miker83z Jan 20, 2026
abad653
fix(e2e): AA receiving objects tests
miker83z Jan 20, 2026
b575c03
fix(move-build): make FnInfoKey unique
miker83z Jan 21, 2026
49a5f4e
feat(core): lock receiving objects at signing time
miker83z Jan 21, 2026
9277a76
feat(e2e): add delayed AA account test
miker83z Jan 21, 2026
cecbdde
Revert "feat(core): lock receiving objects at signing time"
miker83z Jan 22, 2026
52017da
Revert "fix(core): tx input loader rejects receiving objects owned by…
miker83z Jan 22, 2026
d5d2f3a
feat(native): receive_object_internal aborts if parent is an AA account
miker83z Jan 22, 2026
0790063
fix(e2e): update tests to support receive_object_internal execution f…
miker83z Jan 22, 2026
362bd89
feat(aa): account can only be created from a fresh object (not passed…
miker83z Jan 22, 2026
e615feb
fix(snapshots)
miker83z Jan 22, 2026
b63f930
temporarely remove fresh_id check
miker83z Jan 23, 2026
db0e397
fix(e2e): update receiving tests
miker83z Jan 23, 2026
e48c157
Revert "temporarely remove fresh_id check"
miker83z Jan 23, 2026
640e223
Revert "fix(snapshots)"
miker83z Jan 23, 2026
91b9539
Revert "feat(aa): account can only be created from a fresh object (no…
miker83z Jan 23, 2026
f9c113a
fix(native): add move_auth flag check in receive_object_internal
miker83z Jan 23, 2026
37ad2a8
feat(protocol): bump transfer_receive_object_cost_base to 100
miker83z Jan 23, 2026
ae68a72
fix(e2e): update aa tests
miker83z Jan 23, 2026
dab65e1
fix(e2e): update comments
miker83z Jan 24, 2026
8d4395e
fix(adapter-tx-tests): update receiving object snap
miker83z Jan 24, 2026
6b5a516
fix(e2e): typo
miker83z Jan 24, 2026
f51894e
update snapshots
miker83z Jan 24, 2026
6d652da
Merge branch 'vm-lang/aa-auth/8805-beta-feature-branch' into vm-lang/…
miker83z Jan 26, 2026
20442a7
fix(iota): use local framework for example test
miker83z 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
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storag

task 6, lines 66-67:
//# abstract --account immshared(2,2) --ptb-inputs object(2,2) receiving(5,0)
mutated: object(2,0), object(2,2), object(5,0)
gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 3351600, storage_rebate: 3351600, non_refundable_storage_fee: 0
Error: Transaction Effects Status: Move Runtime Abort. Location: iota::transfer::receive_impl (function index 12) at offset 0, Abort Code: 5
Debug of error: MoveAbort(MoveLocation { module: ModuleId { address: iota, name: Identifier("transfer") }, function: 12, instruction: 0, function_name: Some("receive_impl") }, 5) at command Some(0)
Copy link
Contributor

@Dkwcs Dkwcs Jan 26, 2026

Choose a reason for hiding this comment

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

Does this error explain the receiving case enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, but that's the only one we could obtain through a native function I guess.


task 7, line 69:
//# view-object 5,0
Owner: Account Address ( a_account )
Version: 5
Version: 4
Contents: iota::coin::Coin<iota::iota::IOTA> {
id: iota::object::UID {
id: iota::object::ID {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ public fun receive_object(
let received_coin = transfer::public_receive(&mut self.id, coin);
transfer::public_transfer(received_coin, self.account_address());
}

/// Receive an object that was previously sent to this AbstractAccount.
/// This variant does not check the transaction sender.
public fun receive_object_without_sender_check(
self: &mut AbstractAccount,
coin: transfer::Receiving<Coin<IOTA>>,
_ctx: &TxContext,
) {
let received_coin = transfer::public_receive(&mut self.id, coin);
transfer::public_transfer(received_coin, self.account_address());
}

// === Admin Functions ===

/// Check that the sender of this transaction is the account.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) 2025 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

module abstract_account::abstract_account_keyed;

use abstract_account::abstract_account::{Self, AbstractAccount};
use abstract_account::basic_keyed_aa;
use iota::auth_context::AuthContext;
use iota::authenticator_function::AuthenticatorFunctionRefV1;

// === Errors ===

// === Constants ===

// === Structs ===

// === Events ===

// === Method Aliases ===

// === Public Functions ===

/// Creates a new `AbstractAccount` as a shared object with the given authenticator.
///
/// `authenticator` is expected to have a signature like the following:
///
/// public fun authenticate(self: &AbstractAccount, signature: vector<u8>, _: &AuthContext, _: &TxContext) { ... }
///
/// to allow to verify the `signature` parameter against the public key stored in the account.
///
/// There are several ready-made authenticators available in this module:
/// - `authenticate_ed25519`
/// - `authenticate_secp256k1`
/// - `authenticate_secp256r1`
public fun create(
public_key: vector<u8>,
authenticator: AuthenticatorFunctionRefV1<AbstractAccount>,
ctx: &mut TxContext,
) {
abstract_account::builder(authenticator, ctx)
.add_dynamic_field(basic_keyed_aa::owner_public_key(), public_key)
.build();
}

/// Rotates the account owner public key to a new one as well as the authenticator.
/// Once this function is called, the previous public key and authenticator are no longer valid.
/// Only the account itself can call this function.
public fun rotate_public_key(
account: &mut AbstractAccount,
public_key: vector<u8>,
authenticator: AuthenticatorFunctionRefV1<AbstractAccount>,
ctx: &TxContext,
) {
// Update the account owner public key dynamic field. It is expected that the field already exists.
account.replace_field(basic_keyed_aa::owner_public_key(), public_key, ctx);

// Update the account authenticator dynamic field. It is expected that the field already exists.
account.rotate_auth_function_ref_v1(authenticator, ctx);
}

/// Ed25519 signature authenticator.
#[authenticator]
public fun authenticate_ed25519(
account: &AbstractAccount,
signature: vector<u8>,
actx: &AuthContext,
ctx: &TxContext,
) {
// Check the signature.
basic_keyed_aa::authenticate_ed25519(
&signature,
borrow_public_key(account),
actx,
ctx,
);
}

/// Secp256k1 signature authenticator.
#[authenticator]
public fun authenticate_secp256k1(
account: &AbstractAccount,
signature: vector<u8>,
actx: &AuthContext,
ctx: &TxContext,
) {
// Check the signature.
basic_keyed_aa::authenticate_secp256k1(
&signature,
borrow_public_key(account),
actx,
ctx,
);
}

/// Secp256r1 signature authenticator.
#[authenticator]
public fun authenticate_secp256r1(
account: &AbstractAccount,
signature: vector<u8>,
actx: &AuthContext,
ctx: &TxContext,
) {
// Check the signature.
basic_keyed_aa::authenticate_secp256r1(
&signature,
borrow_public_key(account),
actx,
ctx,
);
}

/// Free access, do nothing.
#[authenticator]
public fun authenticate_free_access(_: &AbstractAccount, _: &AuthContext, _: &TxContext) {}

// === View Functions ===

/// An utility function to borrow the account-related public key.
public fun borrow_public_key(account: &AbstractAccount): &vector<u8> {
account.borrow_field(basic_keyed_aa::owner_public_key())
}

// === Admin Functions ===

// === Package Functions ===

// === Private Functions ===

// === Test Functions ===
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

module abstract_account::basic_keyed_aa;

use abstract_account::abstract_account::{Self, AbstractAccount, ensure_tx_sender_is_account};
use iota::auth_context::AuthContext;
use iota::authenticator_function::AuthenticatorFunctionRefV1;
use iota::ecdsa_k1;
use iota::ecdsa_r1;
use iota::ed25519;
Expand Down Expand Up @@ -33,116 +31,58 @@ public struct OwnerPublicKey has copy, drop, store {}

// === Public Functions ===

/// Creates a new `AbstractAccount` as a shared object with the given authenticator.
///
/// `authenticator` is expected to have a signature like the following:
///
/// public fun authenticate(self: &AbstractAccount, signature: vector<u8>, _: &AuthContext, _: &TxContext) { ... }
///
/// to allow to verify the `signature` parameter against the public key stored in the account.
///
/// There are several ready-made authenticators available in this module:
/// - `authenticate_ed25519`
/// - `authenticate_secp256k1`
/// - `authenticate_secp256r1`
public fun create(
public_key: vector<u8>,
authenticator: AuthenticatorFunctionRefV1<AbstractAccount>,
ctx: &mut TxContext,
) {
abstract_account::builder(authenticator, ctx)
.add_dynamic_field(OwnerPublicKey {}, public_key)
.build();
}

/// Rotates the account owner public key to a new one as well as the authenticator.
/// Once this function is called, the previous public key and authenticator are no longer valid.
/// Only the account itself can call this function.
public fun rotate_public_key(
account: &mut AbstractAccount,
public_key: vector<u8>,
authenticator: AuthenticatorFunctionRefV1<AbstractAccount>,
ctx: &TxContext,
) {
// Update the account owner public key dynamic field. It is expected that the field already exists.
account.replace_field(OwnerPublicKey {}, public_key, ctx);

// Update the account authenticator dynamic field. It is expected that the field already exists.
account.rotate_auth_function_ref_v1(authenticator, ctx);
}

/// Ed25519 signature authenticator.
#[authenticator]
public fun authenticate_ed25519(
account: &AbstractAccount,
signature: vector<u8>,
signature: &vector<u8>,
public_key: &vector<u8>,
_: &AuthContext,
ctx: &TxContext,
) {
// Check that the sender of this transaction is the account.
ensure_tx_sender_is_account(account, ctx);

// Check the signature.
assert!(
ed25519::ed25519_verify(&decode(signature), borrow_public_key(account), ctx.digest()),
ed25519::ed25519_verify(&decode(*signature), public_key, ctx.digest()),
EEd25519VerificationFailed,
);
}

/// Secp256k1 signature authenticator.
#[authenticator]
public fun authenticate_secp256k1(
account: &AbstractAccount,
signature: vector<u8>,
signature: &vector<u8>,
public_key: &vector<u8>,
_: &AuthContext,
ctx: &TxContext,
) {
// Check that the sender of this transaction is the account.
ensure_tx_sender_is_account(account, ctx);

// Check the signature.
assert!(
ecdsa_k1::secp256k1_verify(&decode(signature), borrow_public_key(account), ctx.digest(), 0),
ecdsa_k1::secp256k1_verify(&decode(*signature), public_key, ctx.digest(), 0),
ESecp256k1VerificationFailed,
);
}

/// Secp256r1 signature authenticator.
#[authenticator]
public fun authenticate_secp256r1(
account: &AbstractAccount,
signature: vector<u8>,
signature: &vector<u8>,
public_key: &vector<u8>,
_: &AuthContext,
ctx: &TxContext,
) {
// Check that the sender of this transaction is the account.
ensure_tx_sender_is_account(account, ctx);

// Check the signature.
assert!(
ecdsa_r1::secp256r1_verify(&decode(signature), borrow_public_key(account), ctx.digest(), 0),
ecdsa_r1::secp256r1_verify(&decode(*signature), public_key, ctx.digest(), 0),
ESecp256r1VerificationFailed,
);
}

/// Free access, do nothing.
#[authenticator]
public fun authenticate_free_access(self: &AbstractAccount, _: &AuthContext, ctx: &TxContext) {
// Check that the sender of this transaction is the account.
ensure_tx_sender_is_account(self, ctx);
}

// === View Functions ===

/// An utility function to borrow the account-related public key.
public fun borrow_public_key(account: &AbstractAccount): &vector<u8> {
account.borrow_field(OwnerPublicKey {})
}

// === Admin Functions ===

// === Package Functions ===

public(package) fun owner_public_key(): OwnerPublicKey {
OwnerPublicKey {}
}

// === Private Functions ===

// === Test Functions ===
Loading
Loading