@@ -22,16 +22,19 @@ module ol_framework::activity {
2222
2323 //////// ERROR CODES ///////
2424 /// account not initialized on v8 chain
25- const EACCOUNT_MALFORMED : u64 = 1 ;
25+ const EACCOUNT_NOT_MIGRATED : u64 = 0 ;
26+ /// account has malformed timestamp
27+ const ETIMESTAMP_MALFORMED : u64 = 1 ;
2628
2729 struct Activity has key {
2830 last_touch_usecs: u64 ,
2931 onboarding_usecs: u64 ,
3032 }
3133
3234 /// Initialize the activity timestamp of a user
33- public (friend ) fun lazy_initialize (user: &signer , timestamp: u64 ) {
34- if (!exists <Activity >(signer ::address_of (user))) {
35+ public (friend ) fun lazy_initialize (user: &signer ) {
36+ let timestamp = timestamp::now_seconds ();
37+ if (!is_initialized (signer ::address_of (user))) {
3538 move_to <Activity >(user, Activity {
3639 last_touch_usecs: timestamp,
3740 onboarding_usecs: timestamp
@@ -41,33 +44,32 @@ module ol_framework::activity {
4144
4245 /// Increment the activity timestamp of a user
4346 // NOTE: this serves to gate users who have not onboarded since v8 upgrade
44- public (friend ) fun increment (user: &signer , timestamp: u64 ) acquires Activity {
45-
46- assert !( exists < Activity >( signer :: address_of ( user)), error:: invalid_state ( EACCOUNT_MALFORMED ) );
47+ public (friend ) fun increment (user: &signer ) acquires Activity {
48+ is_initialized ( signer :: address_of (user));
49+ maybe_fix_malformed ( user);
4750
4851 let state = borrow_global_mut <Activity >(signer ::address_of (user));
49- state.last_touch_usecs = timestamp;
52+ state.last_touch_usecs = timestamp:: now_seconds () ;
5053 }
5154
5255 #[view]
5356 /// get the last activity timestamp of a user
5457 public fun get_last_activity_usecs (user: address ): u64 acquires Activity {
55- if (exists < Activity > (user)) {
58+ if (is_initialized (user)) {
5659 let state = borrow_global <Activity >(user);
5760 return state.last_touch_usecs
5861 };
5962 0
6063 }
6164
62- public (friend ) fun maybe_onboard (user_sig: &signer ){
63-
65+ public (friend ) fun maybe_onboard (user_sig: &signer ) {
6466 // genesis accounts should not start at 0
6567 let onboarding_usecs = timestamp::now_seconds ();
6668 if (onboarding_usecs == 0 ) {
6769 onboarding_usecs = 1 ;
6870 };
6971
70- if (!exists < Activity > (signer ::address_of (user_sig))) {
72+ if (!is_initialized (signer ::address_of (user_sig))) {
7173 move_to <Activity >(user_sig, Activity {
7274 last_touch_usecs: 0 , // how we identify if a users has used the account after a peer created it.
7375 onboarding_usecs,
@@ -78,19 +80,56 @@ module ol_framework::activity {
7880 /// migrate or heal a pre-v8 account
7981 public (friend ) fun migrate (user_sig: &signer ) acquires Activity {
8082 let addr = signer ::address_of (user_sig);
81- if (!exists < Activity > (addr)) {
83+ if (!is_initialized (addr)) {
8284 move_to <Activity >(user_sig, Activity {
8385 last_touch_usecs: 0 ,
8486 onboarding_usecs: 0 ,
8587 })
86- } else if (is_pre_v8 (addr)) {
88+ };
89+
90+ maybe_fix_malformed (user_sig);
91+
92+ if (is_pre_v8 (addr)) {
8793 // this is a pre-v8 account that might be malformed
8894 let state = borrow_global_mut <Activity >(addr);
8995 state.last_touch_usecs = 0 ;
9096 state.onboarding_usecs = 0 ;
9197 }
9298 }
9399
100+ fun assert_initialized (user: address ) {
101+ // check malformed accounts with microsecs
102+ assert !(exists <Activity >(user), error::invalid_state (EACCOUNT_NOT_MIGRATED ));
103+ }
104+
105+ /// some accounts with transactions immediately after the v8 upgrade
106+ /// may have a malformed timestamp
107+ // NOTE: make this an entry function to allow for
108+ // manual migration
109+ public entry fun maybe_fix_malformed (user: &signer ) acquires Activity {
110+ assert_initialized (signer ::address_of (user));
111+ if (!is_timestamp_secs (signer ::address_of (user))) {
112+ let state = borrow_global_mut <Activity >(signer ::address_of (user));
113+ state.onboarding_usecs = 0 ;
114+ // last_touch_usecs should be set in the transaction_validation
115+ // but we'll set it here to be safe
116+ state.last_touch_usecs = timestamp::now_seconds ();
117+ }
118+ }
119+
120+ /// check for edge cases in initialization during v8 migration
121+ fun is_timestamp_secs (acc: address ): bool acquires Activity {
122+ // check if this is incorrectly in microsecs
123+ if (
124+ get_last_activity_usecs (acc) > 1_000_000_000_000 ||
125+ get_onboarding_usecs (acc) > 1_000_000_000_000
126+ ) {
127+ return false
128+ };
129+
130+ true
131+ }
132+
94133
95134 #[view]
96135 // check if this is an account that has activity
@@ -107,12 +146,16 @@ module ol_framework::activity {
107146
108147 #[view]
109148 public fun get_last_touch_usecs (user: address ): u64 acquires Activity {
149+ assert_initialized (user);
150+
110151 let state = borrow_global <Activity >(user);
111152 state.last_touch_usecs
112153 }
113154
114155 #[view]
115156 public fun get_onboarding_usecs (user: address ): u64 acquires Activity {
157+ // check malformed accounts with microsecs
158+ assert_initialized (user);
116159 let state = borrow_global <Activity >(user);
117160 state.onboarding_usecs
118161 }
@@ -128,10 +171,21 @@ module ol_framework::activity {
128171 #[view]
129172 // check the timestamp prior to v8 launch
130173 public fun is_pre_v8 (user: address ): bool acquires Activity {
174+ assert_initialized (user);
175+
131176 if (testnet::is_testnet ()) {
132177 return false
133178 };
134- get_onboarding_usecs (user) < 1747267200 // Date and time (GMT): Thursday, May 15, 2025 12:00:00 AM
179+
180+ // catch edge cases of malformed timestamp
181+ // should be considered a pre-v8 for
182+ // purposes of triggering a migration
183+ if (!is_timestamp_secs (user)) {
184+ return true
185+ };
186+
187+ get_onboarding_usecs (user) < 1_747_267_200
188+ // Date and time (GMT): Thursday, May 15, 2025 12:00:00 AM
135189 }
136190
137191
0 commit comments