Skip to content

[PLAN] Create staking migration widget #42

@L03TJ3

Description

@L03TJ3

[PLAN] Create staking migration widget

Parent issue: #40
Parent link: #40


Reference File Mapping

External: GoodDollar/GoodProtocol

File Purpose
releases/deployment.json Source of truth for the Fuse staking contract address (FuseStaking) and other protocol addresses needed by the migration adapter

Local: GoodWidget — architecture and packaging

File Purpose
AGENTS.md Scope guardrails, package-boundary rules, PR requirements
ARCHITECTURE.md Provider-first runtime model and package responsibilities
docs/PACKAGING.md Standard widget package shape (index.ts, element.ts, register.ts, tsup.config.ts)
docs/demo-environment.md Storybook and Playwright placement for widget stories/tests
docs/architecture/theming-contract.md Rules for named components, theme targets, and when not to expand public theming surface

Local: GoodWidget — reference package and runtime contracts

File Purpose
packages/citizen-claim-widget/package.json Reference package metadata, dependencies, and exports
packages/citizen-claim-widget/src/CitizenClaimWidget.tsx Reference widget composition, named components, provider usage, and status-driven rendering
packages/citizen-claim-widget/src/adapter.ts Reference adapter pattern (useWallet() → viem clients → SDK/API integration)
packages/citizen-claim-widget/src/widgetRuntimeContract.ts Reference for widget state, action, and host-facing prop contracts
packages/citizen-claim-widget/src/element.ts Reference web-component wrapper
packages/citizen-claim-widget/src/register.ts Reference custom-element registration
packages/citizen-claim-widget/src/index.ts Reference public exports

Local: GoodWidget — shared packages likely to be reused

File Purpose
packages/core/src/provider.tsx GoodWidgetProvider contract for provider-first widget mounting
packages/core/src/hooks.ts useWallet() integration point for wallet address, chain, provider, and connect flow
packages/embed/src/createMiniAppElement.tsx Standard web-component bridge for widget packages
packages/ui/src/index.ts Current reusable primitives/composites already available to new widgets
packages/ui/src/components/Card.ts Primary card primitive
packages/ui/src/components/Button.tsx Primary action button primitives
packages/ui/src/components/Text.ts Shared text primitive
packages/ui/src/components/Heading.ts Shared heading primitive
packages/ui/src/components/TokenAmount.tsx Amount display primitive
packages/ui/src/components-test/Spinner.tsx Existing loading indicator for migration progress
packages/ui/src/components/Toast.tsx Existing transient status messaging pattern

Local: GoodWidget — review/demo coverage references

File Purpose
examples/storybook/src/stories/citizen-claim-widget/CitizenClaimWidget.stories.tsx Reference widget story organization and wallet fixture usage
tests/widgets/citizen-claim-widget/states.spec.ts Reference widget smoke-test structure and screenshot evidence layout

Packages to import

  • @goodwidget/coreGoodWidgetProvider, wallet hooks, provider contract
  • @goodwidget/ui — card, text, heading, button, spinner, toast, stack/layout primitives
  • @goodwidget/embed — web-component bridge
  • viem — wallet/public client creation from the host EIP-1193 provider
  • Migration API client code in the new package (no cross-package API abstraction unless a second widget needs it)

Required States, Flows, and Behaviors

Required states

State Behavior
summary Show staked sG$ amount, migration explanation, and the primary approve/migrate action
approval-pending Show wallet approval in progress on Fuse and block duplicate submissions
approval-failed Show approval failure reason and allow retry
migrating Show the step timeline with one active spinner at a time
success Show migration completion and final confirmation
error Show the failed backend migration step and the surfaced reason
wrong-network Explain the required network and prevent migration until corrected
missing-config Show a clear integrator-facing error when API/operator config is absent

User flows

  1. Read the connected wallet and current Fuse staking position.
  2. If the user has zero staked sG$, render the empty/disabled summary state.
  3. Validate required host config (migrationApiBaseUrl, migrationOperator, optional migrationApiToken).
  4. Validate chain/network before enabling approval.
  5. Trigger ERC-20 approval for the migration operator on Fuse.
  6. Send the approval transaction hash to the migration API after approval confirms.
  7. Poll or subscribe to migration progress and render unstakebridge sentbridge receivedstake.
  8. Preserve completed steps while advancing the active step.
  9. On API or execution failure, surface the failed step and a retry path.
  10. On success, render a completion state that confirms the migrated position is now on Celo savings.

Migration-step behavior

Step UI expectation
unstake Pending spinner while active, checkmark after completion
bridge sent Pending spinner while active, checkmark after completion
bridge received Pending spinner while active, checkmark after completion
stake Pending spinner while active, checkmark after completion

Error and retry behavior

  • Approval rejection stays in approval-failed and does not call the migration API.
  • Migration API failures transition to error with the last known backend step.
  • Retry must restart from a valid user action instead of inventing a workaround state.
  • Completed steps remain visible when a later step fails.

New Components to Create

In packages/staking-migration-widget/ (widget-local by default)

Component Why it belongs here
StakingMigrationWidget.tsx Root widget and state-driven screen switching are migration-specific
adapter.ts / useStakingMigrationAdapter.ts Wallet + approval + migration API orchestration belongs in the feature package
widgetRuntimeContract.ts Host-facing props, statuses, events, and step types are feature-specific
MigrationSummaryCard.tsx Summary copy and CTA are specific to Fuse staking → Celo savings
MigrationProgressTimeline.tsx The four-step migration sequence is specific to this workflow
MigrationStepRow.tsx The step labels/status mapping is domain-specific unless reused elsewhere later
MigrationStatusNotice.tsx Missing-config / wrong-network / backend-error messaging is workflow-specific
element.ts Widget package web-component entry
register.ts Widget package auto-registration entry
index.ts Public exports

Candidate promotions to packages/ui only if a second widget needs the same contract

Component Promotion rule
Generic stepper/timeline row Move to packages/ui only if it can be expressed without migration-specific step names or backend semantics
Generic empty-state card Move to packages/ui only if another widget needs the same structure and props
Generic status banner Move to packages/ui only if it stays semantic and not tied to staking migration copy

Current expectation: keep the first implementation inside packages/staking-migration-widget/ and only promote truly reusable primitives after duplication pressure appears.


Execution Plan

  1. Create packages/staking-migration-widget/ following the same package/export structure as packages/citizen-claim-widget/.
  2. Define the widget runtime contract: required host config, status enum, migration-step enum, success/error event payloads, and adapter state/actions.
  3. Implement the wallet/provider bridge using useWallet() and viem clients, keeping the provider-first architecture intact.
  4. Add the staking-balance read path and zero-balance summary/disabled handling.
  5. Add approval execution for the migration operator on Fuse and gate the API call on confirmed approval success.
  6. Add migration API integration for submitting the approval tx hash and reading migration progress.
  7. Implement the state machine for summary, approval-pending, approval-failed, migrating, success, error, wrong-network, and missing-config.
  8. Build the widget-local UI components for summary, progress timeline, error notices, and completion messaging.
  9. Add element.ts, register.ts, and index.ts so the package ships as React + Web Component like the reference widget.
  10. Add Storybook stories under examples/storybook/src/stories/staking-migration-widget/ covering empty, wrong-network, approval, migrating, success, and error states.
  11. Add Playwright smoke coverage under tests/widgets/staking-migration-widget/ with screenshots for the key visible states.
  12. Validate that no new public theme targets are introduced unless explicitly approved.

Acceptance Criteria

  • A new @goodwidget/staking-migration-widget package exists with standard React/Web Component exports
  • The widget reads the user’s Fuse staked sG$ balance before enabling migration
  • Zero-balance users see an empty state and cannot start migration
  • Missing required config renders a clear missing-config state
  • Wrong-network users see a clear wrong-network state before approval
  • Approval is executed on Fuse for the configured migration operator
  • The migration API is called only after approval succeeds and receives the approval tx hash
  • The widget renders unstake, bridge sent, bridge received, and stake with one active spinner at a time
  • Completed migration steps remain visibly completed while later steps run or fail
  • A failed approval renders approval-failed with retry
  • A failed backend migration renders error with the failed step and surfaced reason
  • A successful migration renders a completion state confirming the move to Celo savings
  • Storybook coverage exists for the main user-visible states
  • Playwright widget coverage exists under tests/widgets/staking-migration-widget/
  • The implementation keeps provider, UI, and embed responsibilities inside existing package boundaries

Human-Reviewer Checklist

  • Confirm the package structure matches the repo’s existing widget packaging model
  • Confirm the Fuse staking contract source is taken from GoodProtocol/releases/deployment.json
  • Review whether any newly introduced component is truly reusable before moving it into packages/ui
  • Verify the adapter does not bypass GoodWidgetProvider / useWallet()
  • Verify approval happens before the migration API call and that approval failure cannot trigger backend migration
  • Verify the step timeline always shows a single active spinner and preserves completed steps
  • Verify the wrong-network and missing-config states are clear to both users and integrators
  • Verify retry behavior restarts from a valid product state instead of relying on hidden workarounds
  • Verify Storybook stories use the shared wallet fixtures and stay under the widget-specific stories folder
  • Verify Playwright evidence is stored under tests/widgets/staking-migration-widget/
  • Verify no new public theme target or package-boundary change was introduced without explicit approval

Metadata

Metadata

Labels

No labels
No labels

Type

No fields configured for Task.

Projects

Status
In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions