Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
feat: add creation timestamp to twin transfer requests for future exp…
…iry handling
  • Loading branch information
sameh-farouk committed Sep 10, 2025
commit 7bb1a4944f1920bf7caeed1f54b47c15be32126e
16 changes: 13 additions & 3 deletions docs/architecture/0024-twin-ownership-transfer.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,23 @@ Introduce a simple two-step transfer protocol implemented in `pallet-tfgrid` tha

### Storage

- `TwinTransferRequests: RequestId -> TwinTransferRequest` (twin_id, old_account, new_account)
- `TwinTransferRequests: RequestId -> TwinTransferRequest` (twin_id, old_account, new_account, created_at)
- `PendingTransferByTwin: TwinId -> RequestId` (enforces one pending request per Twin)
- `TwinTransferRequestID: u64` (monotonic counter)

### Types

- None specific to lifecycle state; a request exists while pending and is removed on accept/cancel.
- `TwinTransferRequest` includes `created_at: BlockNumber` to record when the request was created.
- There is no expiry logic in v1.
- Future cleaners (on_finalize/offchain) can use `created_at` to remove stale items if desired.

### Errors and Semantics

- Request flow is capped at one request per twin. If a request already exists, `request_twin_transfer` returns a single error:
- `TwinTransferPendingExists` ("cancel the existing request first")
- Accept flow has no expiry checks; presence of a matching request and correct signer are sufficient.
- Cancel flow always succeeds for the old owner:
- Removes the request and emits `TwinTransferCanceled`.

## Security Considerations

Expand All @@ -68,4 +78,4 @@ Introduce a simple two-step transfer protocol implemented in `pallet-tfgrid` tha
## References

- Implementation: `substrate-node/pallets/pallet-tfgrid/src/twin_transfer.rs`
- Extrinsics wiring: `substrate-node/pallets/pallet-tfgrid/src/lib.rs` (call_index 40, 41)
- Extrinsics wiring: `substrate-node/pallets/pallet-tfgrid/src/lib.rs` (call_index 40, 41, 42)
3 changes: 2 additions & 1 deletion substrate-node/pallets/pallet-tfgrid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,14 @@ pub mod pallet {
ValueQuery,
>;

// Twin ownership transfer storage types (no status/expiry; pending exists while entry exists)
// Twin ownership transfer storage types with creation timestamp
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
#[scale_info(skip_type_params(T))]
pub struct TwinTransferRequest<T: Config> {
pub twin_id: u32,
pub old_account: T::AccountId,
pub new_account: T::AccountId,
pub created_at: BlockNumberFor<T>,
}

#[pallet::storage]
Expand Down
1 change: 0 additions & 1 deletion substrate-node/pallets/pallet-tfgrid/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const GIGABYTE: u64 = 1024 * 1024 * 1024;
use crate::pallet::{
PendingTransferByTwin, TwinIdByAccountID, TwinTransferRequestID, TwinTransferRequests, Twins,
};
use pallet_balances::Pallet as BalancesPallet;
use frame_support::traits::ReservableCurrency;

#[test]
Expand Down
2 changes: 2 additions & 0 deletions substrate-node/pallets/pallet-tfgrid/src/twin_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ impl<T: Config> Pallet<T> {
req_id = req_id.saturating_add(1);
TwinTransferRequestID::<T>::put(req_id);

let now = <frame_system::Pallet<T>>::block_number();
let request = TwinTransferRequest::<T> {
twin_id,
old_account: old_account.clone(),
new_account: new_account.clone(),
created_at: now,
};

TwinTransferRequests::<T>::insert(req_id, &request);
Expand Down