Skip to content

Continuation of Onramper widget (#605): testing and QA - #644

Merged
L03TJ3 merged 3 commits into
masterfrom
draft-buygd-testing
Aug 6, 2026
Merged

Continuation of Onramper widget (#605): testing and QA#644
L03TJ3 merged 3 commits into
masterfrom
draft-buygd-testing

Conversation

@L03TJ3

@L03TJ3 L03TJ3 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Original PR #605

This PR should:

Summary by Sourcery

Integrate the updated Buy G$ flow with the Onramper widget, using the new design and SDK versions while gating purchase functionality behind wallet connection.

New Features:

  • Add a custom animated Buy progress bar component to guide users through purchasing cUSD and its conversion to G$.

Enhancements:

  • Update the Buy G$ page to require a connected wallet, showing contextual copy and the Onramper widget only when an address is present.
  • Improve Buy G$ calculator layout and styling and apply page-specific CSS to optimize the Onramper widget presentation.
  • Adopt newer local versions of the good-design library and web3sdk-v2 package for the Buy G$ experience.

Build:

  • Point @gooddollar/good-design and @gooddollar/web3sdk-v2 dependencies to local tarball builds for testing the new widget integration.

…nramper widget (#605)

* fix: Prevent Uniswap widget crashes on 100% price impact trades

* updated patch_reference.md

* separate concerns

* Revert "updated patch_reference.md"

This reverts commit ad8846f.

* Add clean uniswap patch for 100% price impact fix

* Add clean uniswap patch for price impact fix

* fix: handle uniswap widget error

* Update gitignore

* updated gitignore file

* updates suggest by yhy the reviewer

* Update .gitignore

Co-authored-by: Lewis B <lewis@ikigaistudios.eu>

* feat: implement Buy G$ page with progress bar and Onramper widget

* remove: removed fallback on posthog

* chore: update localization catalogs

* rm: Updated gitignore file

* fix: fixing critical bugs and standards

* rm: remove falback for posthog

* fix: feedback fix on security, performance, functionality and design

* fix: security and functionality fix

* fix: address all Korbit AI review feedback for Buy G$ feature

* fix: connect widget events to progress bar and fix swap lock bug

* Update src/components/CustomGdOnramperWidget/CustomOnramper.tsx

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix: show wallet connection placeholder on Buy G$ page

* fix: update BuyGD to use new wallet connection system

* refactor: use GdOnramperWidget from good-design package

---------

Co-authored-by: Lewis B <lewis@ikigaistudios.eu>
Co-authored-by: LewisB <lewis@gooddollar.org>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploying goodprotocolui with  Cloudflare Pages  Cloudflare Pages

Latest commit: dd41aa5
Status: ✅  Deploy successful!
Preview URL: https://b20d2272.goodprotocolui.pages.dev
Branch Preview URL: https://draft-buygd-testing.goodprotocolui.pages.dev

View logs

@L03TJ3
L03TJ3 marked this pull request as ready for review August 6, 2026 13:02
@L03TJ3
L03TJ3 requested a review from a team August 6, 2026 13:02
@L03TJ3
L03TJ3 merged commit b63ca89 into master Aug 6, 2026
7 checks passed
@L03TJ3
L03TJ3 deleted the draft-buygd-testing branch August 6, 2026 13:02

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

Fixed security issues:

  • browserify-sign (link)

  • pbkdf2 (link)

  • svgo (link)

  • underscore (link)

  • The new BuyProgressBar component hardcodes English step labels; consider wiring this through your existing i18n macros so the onramp flow is consistently localized.

  • BuyGD.css uses very broad selectors (e.g. .progress-bar, .stepper, [class*="progress"]) that will hide progress UI globally; it would be safer to scope these rules to the Buy G$ page or the specific Onramper container to avoid unintended side effects.

  • package.json now points @gooddollar/good-design and @gooddollar/web3sdk-v2 to local file: temp-packages; please confirm this is intended for the main branch and not just for local testing, or swap back to the published versions before merging.

Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new BuyProgressBar component hardcodes English step labels; consider wiring this through your existing i18n macros so the onramp flow is consistently localized.
- BuyGD.css uses very broad selectors (e.g. .progress-bar, .stepper, [class*="progress"]) that will hide progress UI globally; it would be safer to scope these rules to the Buy G$ page or the specific Onramper container to avoid unintended side effects.
- package.json now points @gooddollar/good-design and @gooddollar/web3sdk-v2 to local file: temp-packages; please confirm this is intended for the main branch and not just for local testing, or swap back to the published versions before merging.

## Individual Comments

### Comment 1
<location path="src/pages/gd/BuyGD/BuyGD.css" line_range="21-27" />
<code_context>
+}
+
+/* Alternative approach - hide progress elements by common selectors */
+.progress-bar,
+.stepper,
+.steps-container,
+[class*="progress"],
+[class*="stepper"],
+[class*="step"] {
+    display: none !important;
+}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Global selectors for `.progress-bar` / `.stepper` etc. are likely to hide unrelated UI across the app.

These selectors apply `display: none !important;` to very generic class names and substring matches, which can unintentionally hide progress/stepper components elsewhere in the app or from third‑party libraries. Please scope these rules under a dedicated Onramper wrapper (e.g. a specific container id/class or iframe context) so they only affect the embedded widget.
</issue_to_address>

### Comment 2
<location path="src/components/BuyProgressBar/index.tsx" line_range="108-117" />
<code_context>
+    const getLineProps = (stepNumber: number, lineIndex: number) => {
</code_context>
<issue_to_address>
**suggestion:** The `stepNumber` parameter in `getLineProps` is unused and the line positioning relies on brittle magic percentages.

This makes the signature misleading and tightly couples the layout to exactly three steps, so any change to `steps` (length or spacing) will likely break line alignment. Please either compute line positions based on `steps.length` and the actual flex layout, or explicitly constrain/document a fixed steps structure, and remove the unused `stepNumber` parameter.

Suggested implementation:

```typescript
    const getCircleProps = (status: string) => {
        return circlePropsMap[status as keyof typeof circlePropsMap] || circlePropsMap.pending
    }

    /**
     * Returns style props for the connecting line segments between steps.
     *
     * This implementation assumes a fixed three-step layout where:
     * - lineIndex = 0 is the line between step 1 and 2
     * - lineIndex = 1 is the line between step 2 and 3
     *
     * If the steps structure changes (length or spacing), this function
     * must be updated accordingly or refactored to derive positions from
     * the steps array and the flex layout.
     */
    const getLineProps = (lineIndex: number) => {
        // Line between step 1 and 2 (lineIndex = 0)
        if (lineIndex === 0) {
            if (currentStep === 1 && isLoading) {
                // Animation state: "1 Blue with progress bar animation"
                return {
                    bg: 'blue.500',
                    width: `${animatedWidth}%`,
                    transition: 'width 0.1s ease-out',
                }
            } else if (currentStep >= 2) {

```

1. Remove the `stepNumber` argument from every call site of `getLineProps` in `src/components/BuyProgressBar/index.tsx`, so calls become `getLineProps(lineIndex)` instead of `getLineProps(stepNumber, lineIndex)`.
2. If there is a `steps` array or dynamic steps layout elsewhere in the file, consider refactoring `getLineProps` to derive any non-animated widths/positions from `steps.length` and the flex container (for example, by using `flex="1"` or `width="100%"` on line containers) instead of hard-coded percentage widths.
3. If additional branches inside `getLineProps` rely on hard-coded percentages tied to specific step indexes, update their comments to match the documented three-step assumption or refactor them to use layout-based calculations as in point 2.
</issue_to_address>

### Comment 3
<location path="src/components/BuyProgressBar/index.tsx" line_range="66" />
<code_context>
+        return 'pending'
+    }
+
+    // Memoize circle props objects to avoid recreation on every render
+    const circlePropsMap = useMemo(
+        () => ({
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying the progress bar by removing unnecessary memoization and bespoke status logic, deriving connector styles from step status, using flex-based layout, and optionally replacing JS-driven animation with CSS-only animation.

You can keep the same behavior while simplifying a few areas:

1. **Drop `useMemo` for `circlePropsMap`**

The object is static and cheap to recreate; memoization here adds indirection without benefit.

```ts
// Remove useMemo import and hook, and define once
const circlePropsMap = {
  completed: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'blue.500',
  },
  active: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'blue.500',
  },
  loading: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'blue.500',
    borderWidth: 3,
    borderColor: 'blue.200',
    animation: 'pulse 2s infinite',
  },
  pending: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'gray.300',
  },
}

const getCircleProps = (status: StepStatus) =>
  circlePropsMap[status] ?? circlePropsMap.pending
```

2. **Unify status model and remove special casing**

You can represent the step status with a single type and avoid the bespoke `stepNumber === 1` logic by expressing that in the generic rules:

```ts
type StepStatus = 'pending' | 'active' | 'loading' | 'completed'

const getStepStatus = (stepNumber: number): StepStatus => {
  if (stepNumber < currentStep) return 'completed'
  if (stepNumber === currentStep) return isLoading ? 'loading' : 'active'
  return 'pending'
}

// If you need “step 1 is always blue”, encode it via styling instead of status branching:
const isFirstStep = stepNumber === 1
const circleProps = {
  ...getCircleProps(status),
  bg: isFirstStep || status !== 'pending' ? 'blue.500' : 'gray.300',
}
```

This keeps the “step 1 always blue when not pending” behavior but simplifies the state logic.

3. **Simplify line state based on right-hand step status**

Instead of separate `lineIndex` and `currentStep` branches, derive line props from the status of the step it leads to:

```ts
const getLinePropsForStep = (toStepNumber: number): { bg: string; width: string } => {
  const status = getStepStatus(toStepNumber)

  if (status === 'loading') {
    return {
      bg: 'blue.500',
      width: `${animatedWidth}%`,
    }
  }
  if (status === 'completed' || status === 'active') {
    return {
      bg: 'blue.500',
      width: '100%',
    }
  }
  return {
    bg: 'gray.300',
    width: '100%',
  }
}

// usage:
<Box height="100%" {...getLinePropsForStep(step.number + 1)} borderRadius="1px" />
```

This removes the need for `lineIndex` and makes the “state machine” easier to follow.

4. **Use flex instead of magic percentage positioning**

You can avoid the `33.33`/`16.67` calculations by making the connectors flex between circles:

```tsx
<HStack alignItems="center" justifyContent="space-between">
  {steps.map((step, index) => {
    const status = getStepStatus(step.number)

    return (
      <React.Fragment key={step.number}>
        <Box alignItems="center">
          {/* circle + label */}
        </Box>

        {index < steps.length - 1 && (
          <Box flex={1} mx={2} height="2px" bg="gray.300">
            <Box height="100%" {...getLinePropsForStep(step.number + 1)} borderRadius="1px" />
          </Box>
        )}
      </React.Fragment>
    )
  })}
</HStack>
```

This keeps the same visual intent but removes manual `left`/`right` positioning.

5. **Optional: replace `setInterval` with CSS-only animation**

If acceptable, you can avoid interval management entirely and keep the loading bar animated via CSS:

```tsx
const getLinePropsForStep = (toStepNumber: number) => {
  const status = getStepStatus(toStepNumber)

  if (status === 'loading') {
    return {
      bg: 'blue.500',
      width: '100%',
      // assuming a keyframe like `@keyframes loadingBar { from { width: 0 } to { width: 100% } }`
      animation: 'loadingBar 2s infinite',
    }
  }
  // active/completed/pending same as before, no extra state
}
```

This removes `animatedWidth` state and `useEffect` while preserving the animated look.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +21 to +27
.progress-bar,
.stepper,
.steps-container,
[class*="progress"],
[class*="stepper"],
[class*="step"] {
display: none !important;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Global selectors for .progress-bar / .stepper etc. are likely to hide unrelated UI across the app.

These selectors apply display: none !important; to very generic class names and substring matches, which can unintentionally hide progress/stepper components elsewhere in the app or from third‑party libraries. Please scope these rules under a dedicated Onramper wrapper (e.g. a specific container id/class or iframe context) so they only affect the embedded widget.

Comment on lines +108 to +117
const getLineProps = (stepNumber: number, lineIndex: number) => {
// Line between step 1 and 2 (lineIndex = 0)
if (lineIndex === 0) {
if (currentStep === 1 && isLoading) {
// Animation state: "1 Blue with progress bar animation"
return {
bg: 'blue.500',
width: `${animatedWidth}%`,
transition: 'width 0.1s ease-out',
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The stepNumber parameter in getLineProps is unused and the line positioning relies on brittle magic percentages.

This makes the signature misleading and tightly couples the layout to exactly three steps, so any change to steps (length or spacing) will likely break line alignment. Please either compute line positions based on steps.length and the actual flex layout, or explicitly constrain/document a fixed steps structure, and remove the unused stepNumber parameter.

Suggested implementation:

    const getCircleProps = (status: string) => {
        return circlePropsMap[status as keyof typeof circlePropsMap] || circlePropsMap.pending
    }

    /**
     * Returns style props for the connecting line segments between steps.
     *
     * This implementation assumes a fixed three-step layout where:
     * - lineIndex = 0 is the line between step 1 and 2
     * - lineIndex = 1 is the line between step 2 and 3
     *
     * If the steps structure changes (length or spacing), this function
     * must be updated accordingly or refactored to derive positions from
     * the steps array and the flex layout.
     */
    const getLineProps = (lineIndex: number) => {
        // Line between step 1 and 2 (lineIndex = 0)
        if (lineIndex === 0) {
            if (currentStep === 1 && isLoading) {
                // Animation state: "1 Blue with progress bar animation"
                return {
                    bg: 'blue.500',
                    width: `${animatedWidth}%`,
                    transition: 'width 0.1s ease-out',
                }
            } else if (currentStep >= 2) {
  1. Remove the stepNumber argument from every call site of getLineProps in src/components/BuyProgressBar/index.tsx, so calls become getLineProps(lineIndex) instead of getLineProps(stepNumber, lineIndex).
  2. If there is a steps array or dynamic steps layout elsewhere in the file, consider refactoring getLineProps to derive any non-animated widths/positions from steps.length and the flex container (for example, by using flex="1" or width="100%" on line containers) instead of hard-coded percentage widths.
  3. If additional branches inside getLineProps rely on hard-coded percentages tied to specific step indexes, update their comments to match the documented three-step assumption or refactor them to use layout-based calculations as in point 2.

return 'pending'
}

// Memoize circle props objects to avoid recreation on every render

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider simplifying the progress bar by removing unnecessary memoization and bespoke status logic, deriving connector styles from step status, using flex-based layout, and optionally replacing JS-driven animation with CSS-only animation.

You can keep the same behavior while simplifying a few areas:

  1. Drop useMemo for circlePropsMap

The object is static and cheap to recreate; memoization here adds indirection without benefit.

// Remove useMemo import and hook, and define once
const circlePropsMap = {
  completed: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'blue.500',
  },
  active: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'blue.500',
  },
  loading: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'blue.500',
    borderWidth: 3,
    borderColor: 'blue.200',
    animation: 'pulse 2s infinite',
  },
  pending: {
    size: '12',
    mb: 2,
    justifyContent: 'center',
    alignItems: 'center',
    bg: 'gray.300',
  },
}

const getCircleProps = (status: StepStatus) =>
  circlePropsMap[status] ?? circlePropsMap.pending
  1. Unify status model and remove special casing

You can represent the step status with a single type and avoid the bespoke stepNumber === 1 logic by expressing that in the generic rules:

type StepStatus = 'pending' | 'active' | 'loading' | 'completed'

const getStepStatus = (stepNumber: number): StepStatus => {
  if (stepNumber < currentStep) return 'completed'
  if (stepNumber === currentStep) return isLoading ? 'loading' : 'active'
  return 'pending'
}

// If you need “step 1 is always blue”, encode it via styling instead of status branching:
const isFirstStep = stepNumber === 1
const circleProps = {
  ...getCircleProps(status),
  bg: isFirstStep || status !== 'pending' ? 'blue.500' : 'gray.300',
}

This keeps the “step 1 always blue when not pending” behavior but simplifies the state logic.

  1. Simplify line state based on right-hand step status

Instead of separate lineIndex and currentStep branches, derive line props from the status of the step it leads to:

const getLinePropsForStep = (toStepNumber: number): { bg: string; width: string } => {
  const status = getStepStatus(toStepNumber)

  if (status === 'loading') {
    return {
      bg: 'blue.500',
      width: `${animatedWidth}%`,
    }
  }
  if (status === 'completed' || status === 'active') {
    return {
      bg: 'blue.500',
      width: '100%',
    }
  }
  return {
    bg: 'gray.300',
    width: '100%',
  }
}

// usage:
<Box height="100%" {...getLinePropsForStep(step.number + 1)} borderRadius="1px" />

This removes the need for lineIndex and makes the “state machine” easier to follow.

  1. Use flex instead of magic percentage positioning

You can avoid the 33.33/16.67 calculations by making the connectors flex between circles:

<HStack alignItems="center" justifyContent="space-between">
  {steps.map((step, index) => {
    const status = getStepStatus(step.number)

    return (
      <React.Fragment key={step.number}>
        <Box alignItems="center">
          {/* circle + label */}
        </Box>

        {index < steps.length - 1 && (
          <Box flex={1} mx={2} height="2px" bg="gray.300">
            <Box height="100%" {...getLinePropsForStep(step.number + 1)} borderRadius="1px" />
          </Box>
        )}
      </React.Fragment>
    )
  })}
</HStack>

This keeps the same visual intent but removes manual left/right positioning.

  1. Optional: replace setInterval with CSS-only animation

If acceptable, you can avoid interval management entirely and keep the loading bar animated via CSS:

const getLinePropsForStep = (toStepNumber: number) => {
  const status = getStepStatus(toStepNumber)

  if (status === 'loading') {
    return {
      bg: 'blue.500',
      width: '100%',
      // assuming a keyframe like `@keyframes loadingBar { from { width: 0 } to { width: 100% } }`
      animation: 'loadingBar 2s infinite',
    }
  }
  // active/completed/pending same as before, no extra state
}

This removes animatedWidth state and useEffect while preserving the animated look.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd41aa55d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)}
</Text>

<GdOnramperWidget onEvents={handleEvents} apiKey={process.env.REACT_APP_ONRAMPER_KEY} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin the onramp contract environment to production

When this page runs in QA, staging, or the default development-celo environment, omitting env="production" makes the upgraded widget's useBuyGd hook derive its helper from the non-production contract deployment, even though Onramper still purchases real CUSD_CELO on Celo mainnet. The immediately preceding QA integration supplied this override; without it, funds can be directed to the wrong helper/swap deployment and fail to produce the user's production G$, so pass the production contract environment explicitly.

Useful? React with 👍 / 👎.

)}
</Text>

<GdOnramperWidget onEvents={handleEvents} apiKey={process.env.REACT_APP_ONRAMPER_KEY} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the non-production testing controls

In non-production deployments this call no longer passes isTesting={!isProd}, while GdOnramperWidget defaults isTesting to false and only renders its Next/reset controls when it is true. Consequently the QA and preview environments targeted by this change cannot simulate the received-funds and swap steps without making a real fiat purchase, removing the existing stepper test path.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants