Description
When a creator account is compromised or an invoice is transferred as part of a business handover, there is no on-chain path to reassign the creator role, forcing the original account to remain the sole authorized administrator indefinitely. The contract needs a two-step creator migration — nomination by the current creator followed by explicit acceptance by the successor — to prevent accidental or coerced transfers.
Technical Context
contracts/split/src/lib.rs — add nominate_new_creator(invoice_id: u64, successor: Address) and accept_creator_role(invoice_id: u64) #[contractimpl] entry points; contracts/split/src/storage.rs — add DataKey::PendingCreator(u64) to hold the nominated address until acceptance; contracts/split/src/auth.rs — env.require_auth(¤t_creator) in nominate_new_creator, env.require_auth(&pending_creator) in accept_creator_role; contracts/split/src/events.rs — emit creator_nominated(invoice_id, successor) and creator_migrated(invoice_id, old, new).
Acceptance Criteria
Description
When a creator account is compromised or an invoice is transferred as part of a business handover, there is no on-chain path to reassign the
creatorrole, forcing the original account to remain the sole authorized administrator indefinitely. The contract needs a two-step creator migration — nomination by the current creator followed by explicit acceptance by the successor — to prevent accidental or coerced transfers.Technical Context
contracts/split/src/lib.rs— addnominate_new_creator(invoice_id: u64, successor: Address)andaccept_creator_role(invoice_id: u64)#[contractimpl]entry points;contracts/split/src/storage.rs— addDataKey::PendingCreator(u64)to hold the nominated address until acceptance;contracts/split/src/auth.rs—env.require_auth(¤t_creator)innominate_new_creator,env.require_auth(&pending_creator)inaccept_creator_role;contracts/split/src/events.rs— emitcreator_nominated(invoice_id, successor)andcreator_migrated(invoice_id, old, new).Acceptance Criteria
nominate_new_creator()writessuccessortoDataKey::PendingCreator(invoice_id)and requires auth from the currentInvoice.creatoraccept_creator_role()overwritesInvoice.creatorwith the pending address, deletesDataKey::PendingCreator, and requires auth from the nominated successoraccept_creator_role()is rejected by therequire_authcheck without mutating statenominate_new_creator()again with a different addresscontracts/split/src/tests/creator_migration_tests.rscover: successful two-step migration, unauthorized acceptance, nomination override, and acceptance on a deleted invoice (must returnError::InvoiceNotFound)