aptos

How are events ordered within a transaction?


I've got a question regarding events. More specifically, I've been reading this https://aptos.dev/concepts/events/ and came across the following sentence.

These represent event streams or a list of events with each entry containing a sequentially increasing sequence_number beginning at 0, a type, and data.

I assume sequence_number starts at 0 for each block. However, I'm not sure if it's different for different events within the same block. Basically, if there are events E1 and E2 within the same block, can they have the same sequence number?

I recently came across two events within the same block with the exact same sequence number, so I wonder if that's something I'm doing wrong or if it is normal behaviour.

Here's an example https://explorer.aptoslabs.com/txn/17809383/events?network=mainnet

All three events have the same sequence number. The question, then, is how can we determine the order of those events? There is no other field in the Event definition https://github.com/aptos-labs/aptos-core/blob/main/crates/aptos-protos/proto/aptos/transaction/testing1/v1/transaction.proto#L87

Also, from the same example https://explorer.aptoslabs.com/txn/17809383/events?network=mainnet, it seems like the index of the events is not correct either. the 0x3::token::DepositEvent is at index 1 and 0x3::token::MintTokenEvent at index 2. However, looking into the token module, it seems like MintTokenEvent should happen before DepositEvent.

Is there any suggested way to determine the ordering of events within the same block?


Solution

  • Update 2024-04:


    I think your confusion arises from a misunderstanding about the scope of sequence numbers. Sequence numbers start at zero and increase each time an event is emitted per event stream. An event stream can be identified by the unique combination of:

    1. Account from which the event is being emitted.
    2. Event type, which is the combination of module, address where that module is deployed, and the event itself, e.g. 0x789::my_module::MyEvent.

    This means every time a function emits 0x789::my_module::MyEvent the sequence number will increase by 1.

    In your example, you refer to these three event streams:

    Even though these events were all emitted from the same account (0xb4e07588374bd19370de08bbb6b0537a975bf57ce755d7976e181e5f4e4d0da4) and they're all from the same module (0x3::token), these are still distinct token streams, so you should not expect anything about how they relate, there is no intrinsic order between them. The fact that they all have the same sequence number (125) is coincidental, it is just because whatever function is being called always emit these 3 events together.

    To further clarify:

    To answer part of your question:

    How can we determine the ordering of those events?

    The events emitted by a transaction are stored in the order they were emitted during execution.


    Appendix

    Here is a demonstration that sequence numbers indeed work this way.

    Publish the following code: banool/move-examples.

    Call the add_friend function:

    aptos move run --function-id `yq .profiles.default.account < .aptos/config.yaml`::emit_events::add_friend --args 'string:Jimmy'
    

    Get the creation number that identifies the event stream for AddFriendEvent:

    $ curl https://fullnode.devnet.aptoslabs.com/v1/accounts/0x0e80c7246986f75d56c7a5a8eb12e0eeacf3536db6c0df09299cc1436c290f9f/resource/0x0e80c7246986f75d56c7a5a8eb12e0eeacf3536db6c0df09299cc1436c290f9f::emit_events::FriendStore | jq -r .data.add_friend_events.guid.id.creation_num
    4
    

    Query the event stream:

    $ curl https://fullnode.devnet.aptoslabs.com/v1/accounts/ecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a/events/4 | jq .
    [
      {
        "version": "2011834",
        "guid": {
          "creation_number": "4",
          "account_address": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a"
        },
        "sequence_number": "0",
        "type": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a::emit_events::AddFriendEvent",
        "data": {
          "name": "Jimmy"
        }
      }
    ]
    

    See that because this is the first event emitted in this stream that the sequence number is 0.

    Call the function again:

    aptos move run --function-id `yq .profiles.default.account < .aptos/config.yaml`::emit_events::add_friend --args 'string:Alice'
    

    See that there are now two events in this stream, and the sequence number of the latest event is 1:

    $ curl https://fullnode.devnet.aptoslabs.com/v1/accounts/ecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a/events/4 | jq .
    [
      {
        "version": "2011834",
        "guid": {
          "creation_number": "4",
          "account_address": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a"
        },
        "sequence_number": "0",
        "type": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a::emit_events::AddFriendEvent",
        "data": {
          "name": "Jimmy"
        }
      },
      {
        "version": "2013959",
        "guid": {
          "creation_number": "4",
          "account_address": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a"
        },
        "sequence_number": "1",
        "type": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a::emit_events::AddFriendEvent",
        "data": {
          "name": "Alice"
        }
      }
    ]
    

    Now let's call remove_newest_friend:

    aptos move run --function-id `yq .profiles.default.account < .aptos/config.yaml`::emit_events::remove_newest_friend
    

    If we query the event stream for RemoveNewestFriendEvent, we can see that this is a separate stream. The creation number is different (indicating a different stream) and the sequence number started at 0 again:

    $ curl https://fullnode.devnet.aptoslabs.com/v1/accounts/ecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a/events/5 | jq .
    [
      {
        "version": "2015644",
        "guid": {
          "creation_number": "5",
          "account_address": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a"
        },
        "sequence_number": "0",
        "type": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a::emit_events::RemoveNewestFriendEvent",
        "data": {
          "new_friend_count": "1"
        }
      }
    ]
    

    If we look at the stream of AddFriendEvent, we see that there are no new events and the sequence number of the latest event has not changed:

    [
      {
        "version": "2011834",
        "guid": {
          "creation_number": "4",
          "account_address": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a"
        },
        "sequence_number": "0",
        "type": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a::emit_events::AddFriendEvent",
        "data": {
          "name": "Jimmy"
        }
      },
      {
        "version": "2013959",
        "guid": {
          "creation_number": "4",
          "account_address": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a"
        },
        "sequence_number": "1",
        "type": "0xecbce66aa4b872e4c56d237311802381f8d7da0cb7ea872eaed43560a1f9118a::emit_events::AddFriendEvent",
        "data": {
          "name": "Alice"
        }
      }
    ]