diff --git a/aptos-move/framework/aptos-framework/doc/object.md b/aptos-move/framework/aptos-framework/doc/object.md index 7ae33a425ee25..e3c6f2f70ece3 100644 --- a/aptos-move/framework/aptos-framework/doc/object.md +++ b/aptos-move/framework/aptos-framework/doc/object.md @@ -32,6 +32,7 @@ make it so that a reference to a global object can be returned from a function. - [Struct `TransferRef`](#0x1_object_TransferRef) - [Struct `LinearTransferRef`](#0x1_object_LinearTransferRef) - [Struct `DeriveRef`](#0x1_object_DeriveRef) +- [Struct `TransferPermission`](#0x1_object_TransferPermission) - [Struct `TransferEvent`](#0x1_object_TransferEvent) - [Struct `Transfer`](#0x1_object_Transfer) - [Constants](#@Constants_0) @@ -89,6 +90,7 @@ make it so that a reference to a global object can be returned from a function. - [Function `is_owner`](#0x1_object_is_owner) - [Function `owns`](#0x1_object_owns) - [Function `root_owner`](#0x1_object_root_owner) +- [Function `grant_permission`](#0x1_object_grant_permission) - [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) @@ -133,6 +135,7 @@ make it so that a reference to a global object can be returned from a function. - [Function `is_owner`](#@Specification_1_is_owner) - [Function `owns`](#@Specification_1_owns) - [Function `root_owner`](#@Specification_1_root_owner) + - [Function `grant_permission`](#@Specification_1_grant_permission)
use 0x1::account;
@@ -144,6 +147,7 @@ make it so that a reference to a global object can be returned from a function.
use 0x1::from_bcs;
use 0x1::guid;
use 0x1::hash;
+use 0x1::permissioned_signer;
use 0x1::signer;
use 0x1::transaction_context;
use 0x1::vector;
@@ -496,6 +500,34 @@ Used to create derived objects from a given objects.
+
+
+
+
+## Struct `TransferPermission`
+
+Permission to transfer object with permissioned signer.
+
+
+struct TransferPermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
object: address
+
+-
+
+
+
+
+
@@ -1999,6 +2031,10 @@ hierarchy.
to: address,
) acquires ObjectCore {
let owner_address = signer::address_of(owner);
+ assert!(
+ permissioned_signer::check_permission_exists(owner, TransferPermission { object }),
+ error::permission_denied(EOBJECT_NOT_TRANSFERRABLE)
+ );
verify_ungated_and_descendant(owner_address, object);
transfer_raw_inner(object, to);
}
@@ -2188,6 +2224,10 @@ Allow origin owners to reclaim any objects they previous burnt.
) acquires TombStone, ObjectCore {
let object_addr = object.inner;
assert!(exists<TombStone>(object_addr), error::invalid_argument(EOBJECT_NOT_BURNT));
+ assert!(
+ permissioned_signer::check_permission_exists(original_owner, TransferPermission { object: object_addr }),
+ error::permission_denied(EOBJECT_NOT_TRANSFERRABLE)
+ );
let TombStone { original_owner: original_owner_addr } = move_from<TombStone>(object_addr);
assert!(original_owner_addr == signer::address_of(original_owner), error::permission_denied(ENOT_OBJECT_OWNER));
@@ -2361,6 +2401,38 @@ to determine the identity of the starting point of ownership.
+
+
+
+
+## Function `grant_permission`
+
+
+
+public fun grant_permission<T>(master: &signer, permissioned_signer: &signer, object: object::Object<T>)
+
+
+
+
+
+Implementation
+
+
+public fun grant_permission<T>(
+ master: &signer,
+ permissioned_signer: &signer,
+ object: Object<T>,
+) {
+ permissioned_signer::authorize_unlimited(
+ master,
+ permissioned_signer,
+ TransferPermission { object: object.inner }
+ )
+}
+
+
+
+
@@ -2437,16 +2509,7 @@ to determine the identity of the starting point of ownership.
### Module-level Specification
-pragma aborts_if_is_strict;
-
-
-
-
-
-
-
-
-fun spec_exists_at<T: key>(object: address): bool;
+pragma aborts_if_is_partial;
@@ -3402,4 +3465,32 @@ to determine the identity of the starting point of ownership.
+
+
+
+### Function `grant_permission`
+
+
+public fun grant_permission<T>(master: &signer, permissioned_signer: &signer, object: object::Object<T>)
+
+
+
+
+
+pragma aborts_if_is_partial;
+aborts_if !permissioned_signer::spec_is_permissioned_signer(permissioned_signer);
+aborts_if permissioned_signer::spec_is_permissioned_signer(master);
+aborts_if signer::address_of(master) != signer::address_of(permissioned_signer);
+
+
+
+
+
+
+
+
+fun spec_exists_at<T: key>(object: address): bool;
+
+
+
[move-book]: https://aptos.dev/move/book/SUMMARY
diff --git a/aptos-move/framework/aptos-framework/sources/object.move b/aptos-move/framework/aptos-framework/sources/object.move
index bb6684ff6f430..7f94c73b2ac90 100644
--- a/aptos-move/framework/aptos-framework/sources/object.move
+++ b/aptos-move/framework/aptos-framework/sources/object.move
@@ -28,6 +28,7 @@ module aptos_framework::object {
use aptos_framework::create_signer::create_signer;
use aptos_framework::event;
use aptos_framework::guid;
+ use aptos_framework::permissioned_signer;
friend aptos_framework::coin;
friend aptos_framework::primary_fungible_store;
@@ -165,6 +166,11 @@ module aptos_framework::object {
self: address,
}
+ /// Permission to transfer object with permissioned signer.
+ struct TransferPermission has copy, drop, store {
+ object: address,
+ }
+
/// Emitted whenever the object's owner field is changed.
struct TransferEvent has drop, store {
object: address,
@@ -540,6 +546,10 @@ module aptos_framework::object {
to: address,
) acquires ObjectCore {
let owner_address = signer::address_of(owner);
+ assert!(
+ permissioned_signer::check_permission_exists(owner, TransferPermission { object }),
+ error::permission_denied(EOBJECT_NOT_TRANSFERRABLE)
+ );
verify_ungated_and_descendant(owner_address, object);
transfer_raw_inner(object, to);
}
@@ -629,6 +639,10 @@ module aptos_framework::object {
) acquires TombStone, ObjectCore {
let object_addr = object.inner;
assert!(exists(object_addr), error::invalid_argument(EOBJECT_NOT_BURNT));
+ assert!(
+ permissioned_signer::check_permission_exists(original_owner, TransferPermission { object: object_addr }),
+ error::permission_denied(EOBJECT_NOT_TRANSFERRABLE)
+ );
let TombStone { original_owner: original_owner_addr } = move_from(object_addr);
assert!(original_owner_addr == signer::address_of(original_owner), error::permission_denied(ENOT_OBJECT_OWNER));
@@ -699,6 +713,18 @@ module aptos_framework::object {
obj_owner
}
+ public fun grant_permission(
+ master: &signer,
+ permissioned_signer: &signer,
+ object: Object,
+ ) {
+ permissioned_signer::authorize_unlimited(
+ master,
+ permissioned_signer,
+ TransferPermission { object: object.inner }
+ )
+ }
+
#[test_only]
use std::option::{Self, Option};
@@ -1093,4 +1119,48 @@ module aptos_framework::object {
set_untransferable(&weapon_constructor_ref);
transfer_with_ref(linear_transfer_ref, @0x456);
}
+
+ #[test_only]
+ use aptos_framework::timestamp;
+
+ #[test(creator = @0x123)]
+ fun test_transfer_permission_e2e(
+ creator: &signer,
+ ) acquires ObjectCore {
+ let aptos_framework = account::create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let (_, hero) = create_hero(creator);
+ let (_, weapon) = create_weapon(creator);
+
+ // Create a permissioned signer
+ let creator_permission_handle = permissioned_signer::create_permissioned_handle(creator);
+ let creator_permission_signer = permissioned_signer::signer_from_permissioned_handle(&creator_permission_handle);
+
+ // Grant aaron_permission_signer permission to transfer weapon object
+ grant_permission(creator, &creator_permission_signer, weapon);
+ transfer_to_object(&creator_permission_signer, weapon, hero);
+
+ permissioned_signer::destroy_permissioned_handle(creator_permission_handle);
+ }
+
+ #[test(creator = @0x123)]
+ #[expected_failure(abort_code = 327689, location = Self)]
+ fun test_transfer_no_permission(
+ creator: &signer,
+ ) acquires ObjectCore {
+ let aptos_framework = account::create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let (_, hero) = create_hero(creator);
+ let (_, weapon) = create_weapon(creator);
+
+ // Create a permissioned signer
+ let creator_permission_handle = permissioned_signer::create_permissioned_handle(creator);
+ let creator_permission_signer = permissioned_signer::signer_from_permissioned_handle(&creator_permission_handle);
+
+ transfer_to_object(&creator_permission_signer, weapon, hero);
+
+ permissioned_signer::destroy_permissioned_handle(creator_permission_handle);
+ }
}
diff --git a/aptos-move/framework/aptos-framework/sources/object.spec.move b/aptos-move/framework/aptos-framework/sources/object.spec.move
index 51ae05b568368..6ddef5f6124bb 100644
--- a/aptos-move/framework/aptos-framework/sources/object.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/object.spec.move
@@ -46,7 +46,14 @@ spec aptos_framework::object {
///
///
spec module {
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
+ }
+
+ spec grant_permission {
+ pragma aborts_if_is_partial;
+ aborts_if !permissioned_signer::spec_is_permissioned_signer(permissioned_signer);
+ aborts_if permissioned_signer::spec_is_permissioned_signer(master);
+ aborts_if signer::address_of(master) != signer::address_of(permissioned_signer);
}
spec fun spec_exists_at(object: address): bool;
diff --git a/aptos-move/framework/aptos-token-objects/doc/aptos_token.md b/aptos-move/framework/aptos-token-objects/doc/aptos_token.md
index b3f124c43686e..90298df3cab66 100644
--- a/aptos-move/framework/aptos-token-objects/doc/aptos_token.md
+++ b/aptos-move/framework/aptos-token-objects/doc/aptos_token.md
@@ -864,6 +864,7 @@ With an existing collection, directly mint a soul bound token into the recipient
token::creator(*token) == signer::address_of(creator),
error::permission_denied(ENOT_CREATOR),
);
+
borrow_global<AptosToken>(token_address)
}
@@ -1561,6 +1562,7 @@ With an existing collection, directly mint a soul bound token into the recipient
collection::creator(*collection) == signer::address_of(creator),
error::permission_denied(ENOT_CREATOR),
);
+
borrow_global<AptosCollection>(collection_address)
}
diff --git a/aptos-move/framework/aptos-token-objects/sources/aptos_token.move b/aptos-move/framework/aptos-token-objects/sources/aptos_token.move
index 5fe04786cd1a9..fc955cae413d8 100644
--- a/aptos-move/framework/aptos-token-objects/sources/aptos_token.move
+++ b/aptos-move/framework/aptos-token-objects/sources/aptos_token.move
@@ -373,6 +373,7 @@ module aptos_token_objects::aptos_token {
token::creator(*token) == signer::address_of(creator),
error::permission_denied(ENOT_CREATOR),
);
+
borrow_global