Skip to content

Commit

Permalink
[0/n][transfer-to-object] Transfer to Object Implementation (#12611)
Browse files Browse the repository at this point in the history
## Description 

This PR implements the core transfer-to-object functionality. In
particular it implements the ability to "receive" an object that was
sent to the address (object ID) of another object using one of the
`transfer` or `transfer_public` functions in the `transfer` module.

More detail is given on the programming model in the attached issue so I
will not go into that.

SDK support for receiving objects has been added in the two PRs stacked
on this one:
* #12987 Adds the `Receiving` type to the json-rpc types, and adds
support receiving objects in the Typescript SDK.
* #12988 Adds support for receiving objects in the Rust SDK
* #13420 Adds pruning of the `per_epoch_object_marker` table at epoch
boundaries

## Test Plan 

I've written a number of tests for this that I believe cover things:
* Execution-correctness tests for this in the transactional tests
* Tests for effect computation in the new sui-core
`transfer_to_object.rs` tests (e.g., receive-then-unwrap,
receive-unwrap-wrap, etc).
* Tests for lock-freeness of receiving arguments (i.e., that the object
identified by the `Receiving` argument is not locked at signing) in the
sui-core `transfer_to_object.rs` tests
* Tests that dependencies are correctly registered, and notified in the
transaction manager for `Receiving` arguments to transactions (see new
tests in the `transaction_manager_tests.rs` file).

A more detailed listing of the tests:
* PTBs
    - Receive object and return to PTB
- Do not do anything with the returned (non-drop) value
[`receive_return_object_dont_touch.move`]
- Call transfer object on it
[`receive_return_object_then_transfer.move`]
- Basic "can receive and then do something in the function"
[`basic_receive.move`]
- Duplicate "Receive" arguments to the PTB
[`duplicate_receive_argument.move`]
    - Pass but don't use `Receiving` argument, then later use it in PTB.
        - By immut ref [`pass_receiver_immut_then_reuse.move`]
        - By mut ref [`pass_receiver_mut_then_reuse.move`]
        - By value and returning it [`pass_through_then_receive.move`]
- Various combinations of receivership being passed
[`receive_by_ref.move`]
(checking borrow/borrow_mut, and restore rules for PTB execution)
    - Receive object of different type [`receive_invalid_type.move`]
- Receive object with non-address owner ownership
[`receive_object_owner.move`]
- Reuse of input receiving argument
[`take_receiver_then_try_to_reuse.move`]
* Type malleability [`receive_invalid_param_ty.move`]
    - Pass receiver into a non-receiver type
      - primitive type
      - struct type with same layout
      - struct type with different layout
    - Pass non-receiver into a receiver type
      - primitive type
      - struct type with same layout
      - struct type with different layout
* Resource conservation/Effects calculation (both transactional tests
and sui-core tests for explicit effects checks)
  - Do various things with object after receiving it:
- Immediately place it as a dynamic field
[`receive_dof_and_mutate.move`]
- Immediately add a dynamic field to it
[`receive_add_dof_and_mutate.move`]
- Immediately add a dynamic field to it, add as a dynamic field to
parent object, then mutate both [`receive_add_dof_and_mutate.move`]
    - Immediately transfer it [`receive_and_send_back.move`]
    - Immediately delete it [`receive_and_deleted.move`]
    - Immediately wrap it  [`receive_and_wrap.move`]
    - Immediately abort [`receive_and_abort.move`]
    - Don't use it [`receive_by_value_flow_through.move`]
- Receive multiple times in a row making sure effects stay in-sync as
expected [`receive_multiple_times_in_row.move`]
  - Shared objects
- Make sure we can receive if object is transferred to an object which
is already shared [`shared_parent/basic_receive.move`]
- Make sure we can receive if object is transferred to an object which
is then shared [`shared_parent/transfer_then_share.move`]
- Non-usage of receiving object argument off a shared parent object
[`shared_parent/drop_receiving.move`]
- Receive object off of shared parent, add as dynamic field of shared
parent and then mutate through the parent
[`shared_parent/receive_dof_and_mutate.move`]
- Send and receive the same object to the same shared parent multiple
times [`shared_parent/receive_multiple_times_in_row.move`]
- MVCC -- Test that we calculate contained UIDs correctly when we
receive an
    object. This is tested in [`mvcc/receive_object_dof.move`] and
    [`mvcc/receive_object_split_changes_dof.move`]
- Sui core tests checking explicit parts of the calculated effects to
make sure they match what we expect:
- Immediately unwrap then transfer inner object
[`transfer_to_object_tests.rs/test_tto_unwrap_transfer`]
- Immediately unwrap then delete inner object as well
[`transfer_to_object_tests.rs/test_tto_unwrap_delete`]
- Immediately unwrap then add inner object as dynamic field
[`transfer_to_object_tests.rs/test_tto_unwrap_add_as_dynamic_field`]
- Immediately unwrap, then wrap again -- this is part of the above since
adding a dynamic field wraps the object
- Basic object receive [`transfer_to_object_tests/test_tto_transfer`]
- Pass but don't ise Receiving argument
[`transfer_to_object_tests/test_tto_unused_receiver`]
- Pass by different references
[`transfer_to_object_tests/test_tto_pass_receiving_by_refs`]
- Receive and immediately delete
[`transfer_to_object_tests/test_tto_delete`]
- Receive, wrap, and then transfer wrapped object
[`transfer_to_object_tests/test_tto_wrap`]
* Sui Core for object locking and transaction dependendency calculation
in effects
- Test that receiving object arguments are not locked, and that
different
orders of execution for two certs that want to receive the same argument
(but only one is valid) can both be run in either order, and both return
    the same execution effects in either order
    [`transfer_to_object_tests/test_tt_not_locked`]
  - Test that transaction dependencies are added correctly:
    - Basic test that we add transaction dependendency if we execute
      successfully and receive the object
      [`transfer_to_object_tests/test_tto_valid_dependencies`]
    - Similar case for if we delete the object immediately

[`transfer_to_object_tests/test_tto_valid_dependencies_delete_on_receive`]
- That we don't register the transaction dependendency if we don't
receive
      the object
      [`transfer_to_object_tests/test_tto_dependencies_dont_receive`]
- That we don't register the transaction dependendency if we don't
receive
      the object and we abort

[`transfer_to_object_tests/test_tto_dependencies_dont_receive_but_abort`]
- That we register the dependendency if we received the object, even if
we
      then went on to abort in the transaction
[`transfer_to_object_tests/test_tto_dependencies_receive_and_abort`]
- Dynamic object field spoofing: make sure we don't accidentally
register a
dynamic object field load of an object that we want to receive at a
different version as a receivership of that object (i.e., don't register
      the transaction dependendency)
      [`transfer_to_object_tests/receive_and_dof_interleave`]

## Additional tests 
- PTBs
    - `MakeMoveVec`:
        - create but don't use [receive_many_move_vec.move]
- pass vec by value but don't receive [receive_many_move_vec.move]
- pass vec by ref then use value to receive in later command
[receive_many_move_vec.move]
- Pass vec by mut ref and pop/receive some, then receive rest in other
call [receive_many_move_vec.move]
- Pass vec by mut ref, only receive some [receive_many_move_vec.move]
- Pass vec by value, only receive some [receive_many_move_vec.move]
        - Pass vec by value, receive all [receive_many_move_vec.move]
- Pack receiving tickets into a struct (some/all) then receive
transitively [receive_duo_struct.move]
- Type mismatches:
- Receiving and phony struct with same struct layout and right type args
([receive_invalid_param_ty.move])
- Receiving with mismatched type args [move_vec_receiving_types.move]
- Receiving with multiple different type args
[move_vec_receiving_types.move]
- `TransferObjects`
- Try to transfer receiving ticket [receive_ticket_coin_operations.move]
- `SplitCoins`
- Try to split a receiving ticket [receive_ticket_coin_operations.move]
- `MergeCoins`
- Try to merge a receiving ticket [receive_ticket_coin_operations.move]
    
- MVCC [`receive_object_access_through_parent[dof/df].move`]
- Transaction input checks (in sui-core tests)
- Delete between cert and execution [tests in `test_tto_not_locked`in
the sui-core tests
- Cert denial if sending a transaction where `input_objects \intersect
receiving_object !=
{}` [`test_tto_intersection_input_and_receiving_objects`]
- Type-fixing for receiving arguments [pt_receive_type_fixing.move]

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [X] protocol change
- [X] user-visible impact
- [ ] breaking change for a client SDKs
- [X] breaking change for FNs (FN binary must upgrade)
- [X] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes

Added the ability to receive objects off of another object. This is
currently only turned on in devnet. More information on
transfer-to-object, receiving objects off of other objects, and current SDK support can be
found in the GitHub issue which can be found here:
#12658
  • Loading branch information
tzakian authored Sep 22, 2023
1 parent 965e530 commit e70909d
Show file tree
Hide file tree
Showing 171 changed files with 8,994 additions and 395 deletions.
1 change: 1 addition & 0 deletions crates/simulacrum/src/epoch_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ impl EpochState {
store,
&self.protocol_config,
self.epoch_start_state.reference_gas_price(),
self.epoch(),
transaction.data().transaction_data(),
deny_config,
&self.bytecode_verifier_metrics,
Expand Down
39 changes: 38 additions & 1 deletion crates/simulacrum/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ use sui_types::{
VerifiedCheckpoint,
},
object::{Object, Owner},
storage::{BackingPackageStore, ChildObjectResolver, ObjectStore, ParentSync},
storage::{
BackingPackageStore, ChildObjectResolver, ObjectStore, ParentSync, ReceivedMarkerQuery,
},
transaction::VerifiedTransaction,
};

Expand Down Expand Up @@ -275,6 +277,41 @@ impl ChildObjectResolver for InMemoryStore {

Ok(Some(child_object))
}

fn get_object_received_at_version(
&self,
owner: &ObjectID,
receiving_object_id: &ObjectID,
receive_object_at_version: SequenceNumber,
_epoch_id: EpochId,
) -> sui_types::error::SuiResult<Option<Object>> {
let recv_object = match self.get_object(receiving_object_id).cloned() {
None => return Ok(None),
Some(obj) => obj,
};
if recv_object.owner != Owner::AddressOwner((*owner).into()) {
return Ok(None);
}

if recv_object.version() != receive_object_at_version {
return Ok(None);
}
Ok(Some(recv_object))
}
}

impl ReceivedMarkerQuery for InMemoryStore {
fn have_received_object_at_version(
&self,
_object_id: &ObjectID,
_version: sui_types::base_types::VersionNumber,
_epoch_id: EpochId,
) -> Result<bool, SuiError> {
// In simulation, we always have the object don't have a marker table, and we don't need to
// worry about equivocation protection. So we simply return false if ever asked if we
// received this object.
Ok(false)
}
}

impl GetModule for InMemoryStore {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
processed 14 tasks

init:
A: object(0,0)

task 1 'publish'. lines 6-78:
created: object(1,0)
mutated: object(0,1)
gas summary: computation_cost: 1000000, storage_cost: 12524800, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'run'. lines 80-80:
created: object(2,0), object(2,1), object(2,2), object(2,3), object(2,4), object(2,5), object(2,6), object(2,7)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 15298800, storage_rebate: 0, non_refundable_storage_fee: 0

task 3 'view-object'. lines 82-82:
Owner: Object ID: ( _ )
Version: 2
Contents: sui::dynamic_field::Field<sui::dynamic_object_field::Wrapper<u64>, sui::object::ID> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,0)}}, name: sui::dynamic_object_field::Wrapper<u64> {name: 1u64}, value: sui::object::ID {bytes: fake(2,5)}}

task 4 'view-object'. lines 84-84:
Owner: Object ID: ( fake(2,6) )
Version: 2
Contents: sui::dynamic_field::Field<u64, tto::M1::A> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,1)}}, name: 0u64, value: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: _}}, value: vector[98u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8]}}

task 5 'view-object'. lines 86-86:
Owner: Object ID: ( _ )
Version: 2
Contents: sui::dynamic_field::Field<u64, tto::M1::A> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,2)}}, name: 0u64, value: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: _}}, value: vector[98u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8]}}

task 6 'view-object'. lines 88-88:
Owner: Object ID: ( _ )
Version: 2
Contents: sui::dynamic_field::Field<u64, tto::M1::A> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,3)}}, name: 0u64, value: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: _}}, value: vector[119u8, 114u8, 97u8, 112u8, 112u8, 101u8, 100u8, 95u8, 100u8, 111u8, 102u8]}}

task 7 'view-object'. lines 90-90:
Owner: Account Address ( A )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,4)}}, value: vector[97u8, 95u8, 112u8, 97u8, 114u8, 101u8, 110u8, 116u8]}

task 8 'view-object'. lines 92-92:
Owner: Object ID: ( fake(2,0) )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,5)}}, value: vector[98u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8, 95u8, 100u8, 111u8, 102u8]}

task 9 'view-object'. lines 94-94:
Owner: Account Address ( fake(2,4) )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,6)}}, value: vector[98u8, 95u8, 112u8, 97u8, 114u8, 101u8, 110u8, 116u8]}

task 10 'view-object'. lines 96-99:
Owner: Account Address ( fake(2,4) )
Version: 2
Contents: tto::M1::Wrapper<tto::M1::A> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,7)}}, value: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: _}}, value: vector[119u8, 114u8, 97u8, 112u8, 112u8, 101u8, 100u8]}}

task 11 'run'. lines 100-100:
Error: Transaction Effects Status: Move Runtime Abort. Location: sui::transfer::receive_impl (function index 10) at offset 0, Abort Code: 2
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: sui, name: Identifier("transfer") }, function: 10, instruction: 0, function_name: Some("receive_impl") }, 2), source: Some(VMError { major_status: ABORTED, sub_status: Some(2), message: None, exec_state: None, location: Module(ModuleId { address: sui, name: Identifier("transfer") }), indices: [], offsets: [(FunctionDefinitionIndex(10), 0)] }), command: Some(0) } }

task 12 'run'. lines 102-102:
created: object(12,0)
mutated: object(0,0), object(2,4)
wrapped: object(2,6)
gas summary: computation_cost: 1000000, storage_cost: 4278800, storage_rebate: 3521232, non_refundable_storage_fee: 35568

task 13 'run'. lines 104-104:
created: object(13,0)
mutated: object(0,0), object(2,4)
wrapped: object(2,7)
gas summary: computation_cost: 1000000, storage_cost: 4856400, storage_rebate: 4093056, non_refundable_storage_fee: 41344
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//# init --addresses tto=0x0 --accounts A

//# publish
module tto::M1 {
use sui::object::{Self, UID};
use sui::tx_context::{Self, TxContext};
use sui::transfer::{Self, Receiving};
use sui::dynamic_object_field as dof;
use sui::dynamic_field as df;

const KEY: u64 = 0;
const BKEY: u64 = 1;

struct A has key, store {
id: UID,
value: vector<u8>,
}

struct Wrapper<T: key + store> has key, store {
id: UID,
value: T,
}

public fun start(ctx: &mut TxContext) {
let a_parent = A { id: object::new(ctx), value: b"a_parent" };

let b_parent = A { id: object::new(ctx), value: b"b_parent" };
let b_child = A { id: object::new(ctx), value: b"b_child" };
let b_child_child = A { id: object::new(ctx), value: b"b_child_child" };
let b_child_child_dof = A { id: object::new(ctx), value: b"b_child_child_dof" };

let wrapped_df = A { id: object::new(ctx), value: b"wrapped_dof" };
let to_wrap = A { id: object::new(ctx), value: b"wrapped" };
df::add(&mut to_wrap.id, KEY, wrapped_df);
let wrapped = Wrapper { id: object::new(ctx), value: to_wrap };

df::add(&mut b_child.id, KEY, b_child_child);
dof::add(&mut b_child.id, BKEY, b_child_child_dof);
df::add(&mut b_parent.id, KEY, b_child);

let a_address = object::id_address(&a_parent);
transfer::public_transfer(a_parent, tx_context::sender(ctx));
transfer::public_transfer(b_parent, a_address);
transfer::public_transfer(wrapped, a_address);
}

public entry fun receive_b_parent(a_parent: &mut A, x: Receiving<A>) {
let b_parent = transfer::receive(&mut a_parent.id, x);
df::add(&mut a_parent.id, KEY, b_parent);
let b_parent: &A = df::borrow(&a_parent.id, KEY);
let b_child: &A = df::borrow(&b_parent.id, KEY);
let b_child_child: &A = df::borrow(&b_child.id, KEY);
let b_child_child_dof: &A = dof::borrow(&b_child.id, BKEY);
assert!(a_parent.value == b"a_parent", 0);
assert!(b_child.value == b"b_child", 1);
assert!(b_parent.value == b"b_parent", 2);
assert!(b_child_child.value == b"b_child_child", 3);
assert!(b_child_child_dof.value == b"b_child_child_dof", 4);
}

public entry fun receive_wrapped(a_parent: &mut A, x: Receiving<Wrapper<A>>) {
let wrapped = transfer::receive(&mut a_parent.id, x);
df::add(&mut a_parent.id, BKEY, wrapped);
let wrapped: &Wrapper<A> = df::borrow(&a_parent.id, BKEY);
let wrapped_dof: &A = df::borrow(&wrapped.value.id, KEY);
assert!(wrapped.value.value == b"wrapped", 5);
assert!(wrapped_dof.value == b"wrapped_dof", 6);
}
}

// receive, add, and then access through parent.
// * A dynamic field
// * A dynamic field of a dynamic field
// * A dynamic object field of a dynamic field
// * A dynamic field of wrapped object that was received

//# run tto::M1::start --sender A

//# view-object 2,0

//# view-object 2,1

//# view-object 2,2

//# view-object 2,3

//# view-object 2,4

//# view-object 2,5

//# view-object 2,6

//# view-object 2,7

// E_OBJECT_TYPE_MISMATCH
// Try to load an invalid type that will cause indexing to fail.
//# run tto::M1::receive_b_parent --args object(2,4) receiving(2,7) --sender A

//# run tto::M1::receive_b_parent --args object(2,4) receiving(2,6) --sender A

//# run tto::M1::receive_wrapped --args object(2,4) receiving(2,7) --sender A
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
processed 16 tasks

init:
A: object(0,0)

task 1 'publish'. lines 6-78:
created: object(1,0)
mutated: object(0,1)
gas summary: computation_cost: 1000000, storage_cost: 12509600, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'run'. lines 80-80:
created: object(2,0), object(2,1), object(2,2), object(2,3), object(2,4), object(2,5), object(2,6), object(2,7), object(2,8), object(2,9)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 18749200, storage_rebate: 0, non_refundable_storage_fee: 0

task 3 'view-object'. lines 82-82:
Owner: Object ID: ( fake(2,7) )
Version: 2
Contents: sui::dynamic_field::Field<sui::dynamic_object_field::Wrapper<u64>, sui::object::ID> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,0)}}, name: sui::dynamic_object_field::Wrapper<u64> {name: 0u64}, value: sui::object::ID {bytes: fake(2,4)}}

task 4 'view-object'. lines 84-84:
Owner: Object ID: ( _ )
Version: 2
Contents: sui::dynamic_field::Field<sui::dynamic_object_field::Wrapper<u64>, sui::object::ID> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,1)}}, name: sui::dynamic_object_field::Wrapper<u64> {name: 0u64}, value: sui::object::ID {bytes: fake(2,5)}}

task 5 'view-object'. lines 86-86:
Owner: Object ID: ( fake(2,6) )
Version: 2
Contents: sui::dynamic_field::Field<sui::dynamic_object_field::Wrapper<u64>, sui::object::ID> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,2)}}, name: sui::dynamic_object_field::Wrapper<u64> {name: 0u64}, value: sui::object::ID {bytes: fake(2,7)}}

task 6 'view-object'. lines 88-88:
Owner: Object ID: ( fake(2,7) )
Version: 2
Contents: sui::dynamic_field::Field<u64, tto::M1::A> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,3)}}, name: 1u64, value: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: _}}, value: vector[98u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8, 95u8, 100u8, 102u8]}}

task 7 'view-object'. lines 90-90:
Owner: Object ID: ( fake(2,0) )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,4)}}, value: vector[98u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8]}

task 8 'view-object'. lines 92-92:
Owner: Object ID: ( fake(2,1) )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,5)}}, value: vector[119u8, 114u8, 97u8, 112u8, 112u8, 101u8, 100u8, 95u8, 100u8, 102u8]}

task 9 'view-object'. lines 94-94:
Owner: Account Address ( fake(2,8) )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,6)}}, value: vector[98u8, 95u8, 112u8, 97u8, 114u8, 101u8, 110u8, 116u8]}

task 10 'view-object'. lines 96-96:
Owner: Object ID: ( fake(2,2) )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,7)}}, value: vector[98u8, 95u8, 99u8, 104u8, 105u8, 108u8, 100u8]}

task 11 'view-object'. lines 98-98:
Owner: Account Address ( A )
Version: 2
Contents: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,8)}}, value: vector[97u8, 95u8, 112u8, 97u8, 114u8, 101u8, 110u8, 116u8]}

task 12 'view-object'. lines 100-103:
Owner: Account Address ( fake(2,8) )
Version: 2
Contents: tto::M1::Wrapper<tto::M1::A> {id: sui::object::UID {id: sui::object::ID {bytes: fake(2,9)}}, value: tto::M1::A {id: sui::object::UID {id: sui::object::ID {bytes: _}}, value: vector[119u8, 114u8, 97u8, 112u8, 112u8, 101u8, 100u8]}}

task 13 'run'. lines 104-104:
Error: Transaction Effects Status: Move Runtime Abort. Location: sui::transfer::receive_impl (function index 10) at offset 0, Abort Code: 2
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: sui, name: Identifier("transfer") }, function: 10, instruction: 0, function_name: Some("receive_impl") }, 2), source: Some(VMError { major_status: ABORTED, sub_status: Some(2), message: None, exec_state: None, location: Module(ModuleId { address: sui, name: Identifier("transfer") }), indices: [], offsets: [(FunctionDefinitionIndex(10), 0)] }), command: Some(0) } }

task 14 'run'. lines 106-106:
created: object(14,0)
mutated: object(0,0), object(2,6), object(2,8)
gas summary: computation_cost: 1000000, storage_cost: 6011600, storage_rebate: 3521232, non_refundable_storage_fee: 35568

task 15 'run'. lines 108-108:
created: object(15,0)
mutated: object(0,0), object(2,8), object(2,9)
gas summary: computation_cost: 1000000, storage_cost: 6589200, storage_rebate: 4093056, non_refundable_storage_fee: 41344
Loading

1 comment on commit e70909d

@vercel
Copy link

@vercel vercel bot commented on e70909d Sep 22, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.