Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 10 additions & 6 deletions .github/workflows/deploy_vercel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ jobs:
if: "github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'production'"
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v3
- uses: actions/setup-node@v6
with:
node-version: 20
- uses: actions/checkout@v3
node-version: 24
- uses: actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
Expand All @@ -48,11 +48,15 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: yarn
- name: Enable Yarn
run: corepack enable
- name: Verify Node.js version
run: node --version
- run: yarn install --immutable
- run: yarn vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- run: yarn vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/jest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ jobs:

steps:
- name: Check out Git repository
uses: actions/checkout@v3
uses: actions/checkout@v6

- name: Set up Node
uses: actions/setup-node@v3
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24

- name: Enable Yarn
run: corepack enable

- name: Install dependencies
run: yarn install --immutable
Expand Down
24 changes: 7 additions & 17 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,19 @@ jobs:

steps:
- name: Check out Git repository
uses: actions/checkout@v2
uses: actions/checkout@v6

- name: Set up node
uses: actions/setup-node@v1
uses: actions/setup-node@v6
with:
node-version: 12
always-auth: true
registry-url: https://registry.npmjs.org
node-version: 24
cache: yarn

- name: Set output of cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"

- name: Node dependency cache
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-
- name: Enable Yarn
run: corepack enable

- name: Install dependencies
run: yarn install --frozen-lockfile
run: yarn install --immutable

- name: Run linters
uses: wearerequired/lint-action@77d70b9a07ecb93bc98dc46dc27d96c4f004d035
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/preview_vercel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ jobs:
if: "github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'qa'"
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 20
- uses: actions/checkout@v4
node-version: 24
- uses: actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
Expand All @@ -57,13 +57,17 @@ jobs:
name: preview
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: yarn
- name: Enable Yarn
run: corepack enable
- name: Verify Node.js version
run: node --version
- run: yarn install --immutable
- id: check_branch
run: echo "::set-output name=branch::$(if [[ "${{ github.ref }}" == *"/staging" ]]; then echo 'staging'; else echo 'master'; fi)"
Expand Down
48 changes: 15 additions & 33 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,18 @@ on:
jobs:
integration-tests:
name: Integration tests
runs-on: ubuntu-16.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- uses: actions/setup-node@v1
uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '12'
always-auth: true
registry-url: https://registry.npmjs.org
node-version: 24
cache: yarn

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile
- name: Enable Yarn
run: corepack enable
- run: yarn install --immutable
- run: yarn cypress install
- run: yarn build
env:
Expand All @@ -43,23 +34,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- uses: actions/setup-node@v1
uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '12'
always-auth: true
registry-url: https://registry.npmjs.org
node-version: 24
cache: yarn

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile
- name: Enable Yarn
run: corepack enable
- run: yarn install --immutable
- run: yarn test

1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ https://cocky-dubinsky-430a6c.netlify.app/

### Install Dependencies

Use Node.js 24 and the repository-pinned Yarn 3.6.1.

```bash
yarn
nvm install
nvm use
corepack enable
yarn install --immutable
```

### Run
Expand Down
54 changes: 54 additions & 0 deletions docs/RESERVE_LIMIT_QA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Reserve-limit QA simulation

This development-only path exercises the SDK Reserve-limit error classifier and the existing limit modal. It does not test real trading limits, approvals, mined receipts, or delegated-wallet execution.

## Prerequisites

- Node 24 and Yarn 3.6.1.
- A `GoodWeb3-Mono` checkout on `reserve-and-bridge-fixes` that includes the paired QA API: `Web3Provider` accepts `qa.forceTradeLimits`, and `useWeb3Context()` reports `qa.forceTradeLimits === true` only when the SDK simulation is active and blocks Reserve approval/submission before signer access.
- A connected test account, a valid Celo or XDC Reserve quote, and no token balance or allowance are required. Do not approve, sign, or send a transaction.
- The SDK and design packages must be built and installed locally with yalc; package publication is not required.

## Build and install the paired packages

From the `GoodWeb3-Mono` checkout, first update `reserve-and-bridge-fixes` to the commit that implements the QA API, then run:

```sh
yarn install --immutable
cd packages/sdk-v2
yarn build
yalc publish --push
cd ../good-design
yalc add -D @gooddollar/web3sdk-v2
yarn build
```

From the `GoodProtocolUI` checkout on `reserve-and-rpc-fixes`, run:

```sh
yarn install --immutable
yalc add @gooddollar/web3sdk-v2 @gooddollar/good-design
yarn install
```

If setting up the checkout for the first time, enable Corepack with `corepack enable` before using Yarn.

## Enable, run, and disable

In `GoodProtocolUI`, set this in the ignored `.env.local` file:

```dotenv
REACT_APP_QA_FORCE_TRADE_LIMITS=true
```

Start the Vite development server with `yarn start` and open `http://localhost:3000`. The app must be running in development mode; the setting is ignored by production builds. `REACT_APP_NETWORK` remains unchanged: it selects contract deployment configuration and is not the build-mode switch.

Connect a test account, select Celo or XDC, and enter an amount until a valid Reserve quote is shown. With the QA-capable SDK active, the app shows **QA simulation: Reserve limit** instead of the real Approve/Swap actions, even when the wallet has no balance or allowance. The confirmation is labelled **SIMULATE RESERVE LIMIT**. Confirming it exercises the SDK's injected error, which must happen before signer access. If the SDK is old or does not report the capability as active, the QA button and balance/allowance bypass remain unavailable.

Check that the confirmation closes and the existing Reserve-limit modal identifies the selected chain. Celo provides Uniswap and Ubeswap links; XDC provides XSwap. Close the modal and repeat to check it reopens without a stale error. Links are destinations to inspect only; do not connect or trade there.

For the separate layout check, use browser responsive mode at **375 × 812** on both chains. Confirm the text and links do not overflow, scrolling works, and the close control stays visible. This is an emulated layout check, not mobile transaction QA.

To disable the path, set `REACT_APP_QA_FORCE_TRADE_LIMITS=false` or remove the line, then stop and restart `yarn start`. With the flag off, ordinary balance and allowance restrictions apply.

Do not use `yarn build` or a production deployment to enable the simulation. This path verifies injected limit-error handling only; it does not verify genuine limit behavior, transaction receipts, or delegated-wallet execution.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,10 @@
},
"dependencies": {
"@babel/runtime": "^7.18.9",
"@gooddollar/good-design": "file:temp-packages/@gooddollar-good-design-0.4.52.tgz",
"@gooddollar/good-design": "file:.yalc/@gooddollar/good-design",
"@gooddollar/goodprotocol": "2.2.1",
"@gooddollar/web3sdk": "0.1.59",
"@gooddollar/web3sdk-v2": "file:temp-packages/@gooddollar-web3sdk-v2-0.4.48.tgz",
"@gooddollar/web3sdk-v2": "file:.yalc/@gooddollar/web3sdk-v2",
"@goodsdks/savings-widget": "^1.0.0",
"@headlessui/react": "1.5.0",
"@lingui/format-json": "^4.0.0",
Expand Down Expand Up @@ -257,6 +257,9 @@
"wagmi": "^2.14.13",
"web3": "1.8.2"
},
"engines": {
"node": "24.x"
},
"packageManager": "yarn@3.6.1",
"jest": {
"testEnvironment": "jsdom",
Expand Down
25 changes: 1 addition & 24 deletions src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import '@reach/dialog/styles.css'
import { transparentize } from 'polished'
import React from 'react'
import { isMobile } from 'react-device-detect'
import { animated, useSpring, useTransition } from 'react-spring'
import { useGesture } from 'react-use-gesture'
import { animated, useTransition } from 'react-spring'
import styled, { css } from 'styled-components'

const AnimatedDialogOverlay = animated(DialogOverlay)
Expand Down Expand Up @@ -115,18 +114,6 @@ export default function Modal({
leave: { opacity: 0 },
})

const [{ y }, set] = useSpring(() => ({ y: 0, config: { mass: 1, tension: 210, friction: 20 } }))
const bind = useGesture({
onDrag: (state) => {
set({
y: state.down ? state.movement[1] : 0,
})
if (state.movement[1] > 300 || (state.velocity > 3 && state.direction[1] > 0)) {
onDismiss()
}
},
})

return (
<>
{fadeTransition.map(
Expand All @@ -139,16 +126,6 @@ export default function Modal({
initialFocusRef={initialFocusRef}
>
<StyledDialogContent
{...(isMobile
? {
...bind(),
style: {
transform: y.interpolate(
(y) => `translateY(${(y as number) > 0 ? y : 0}px)`
),
},
}
: {})}
aria-label="dialog content"
minHeight={minHeight}
maxHeight={maxHeight}
Expand Down
40 changes: 40 additions & 0 deletions src/functions/reserveLimitQa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export interface ReserveLimitQaCapability {
forceTradeLimits?: boolean
}

export const isReserveLimitQaRequested = (isDevelopmentBuild: boolean, value?: string): boolean =>
isDevelopmentBuild && value === 'true'

export const isReserveLimitQaActive = (
isDevelopmentBuild: boolean,
value: string | undefined,
capability: ReserveLimitQaCapability | undefined,
hasLimitModal: boolean
): boolean =>
isReserveLimitQaRequested(isDevelopmentBuild, value) && capability?.forceTradeLimits === true && hasLimitModal

export const canOpenReserveLimitQa = ({
active,
chainId,
address,
hasValidQuote,
}: {
active: boolean
chainId?: number
address?: string
hasValidQuote: boolean
}): boolean => active && Boolean(address) && hasValidQuote && (chainId === 42220 || chainId === 50)

export const getReserveActionMode = (
qaActive: boolean,
canStartQaSimulation: boolean,
balanceNotEnough: boolean
): 'simulate-limit' | 'qa-unavailable' | 'insufficient-balance' | 'normal' => {
if (qaActive) return canStartQaSimulation ? 'simulate-limit' : 'qa-unavailable'
return balanceNotEnough ? 'insufficient-balance' : 'normal'
}

export const canUseRealReserveActions = (qaActive: boolean): boolean => !qaActive

export const canConfirmReserveLimitQa = (qaSimulation: boolean, capabilityActive: boolean): boolean =>
!qaSimulation || capabilityActive
Loading
Loading