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
85 changes: 85 additions & 0 deletions maestro/pay-tests/.maestro/pay_usdt_polygon.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
appId: ${APP_ID}
name: WalletConnect Pay - USDT on Polygon (Permit2 approve + pay)
tags:
- pay
- pay-usdt-polygon
---
# Create payment via API using the merchant that offers USDT on Polygon.
# Reuses the shared create-payment.js; the merchant determines which assets
# (USDT on Polygon among them) are offered to the wallet.
- runScript:
file: scripts/create-payment.js
env:
WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_MULTI_NOKYC}
WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_MULTI_NOKYC}

- clearState

- startRecording: "Pay - USDT on Polygon"

# Open wallet, paste payment URL, wait for merchant info / options
- runFlow:
file: flows/pay_open_and_paste_url.yaml

- extendedWaitUntil:
visible:
id: "pay-select-option-header"
timeout: 30000

# Select the USDT-on-Polygon option by its stable network+token testID
# (the merchant may also list USDT on other networks, e.g. Arbitrum, so a plain
# "USDT" text match would pick the wrong row). Scroll it into view first since it
# may be below the fold. USDT on Polygon is a plain ERC-20 (no EIP-3009/2612), so
# it takes the Permit2 approve + pay path — the point of this test. (USDT on
# Arbitrum is EIP-3009 / signature-based and would never trigger an on-chain approve.)
- scrollUntilVisible:
element:
id: "pay-option-usdt-polygon"
direction: DOWN
timeout: 20000
- tapOn:
id: "pay-option-usdt-polygon"

# Review screen must be the Polygon token (testID is keyed by network name)
- assertVisible:
id: "pay-review-token-polygon"

# Verify the pay button shows a fiat amount
- copyTextFrom:
id: "pay-button-pay"
- assertTrue:
condition: "${maestro.copiedText.startsWith('Pay $')}"

# Pay. USDT on Polygon is a Permit2 token, so the wallet sends approve() then payment().
- tapOn:
id: "pay-button-pay"

# Best-effort observation of the approve step. The setup note only renders while a
# token is being set up for the first time (allowance == 0), and it can flash by
# quickly, so this is intentionally soft: a `when: visible` guard with a screenshot
# (never fails) instead of an assertion. If Maestro misses the brief window, or a
# prior-run allowance reset didn't happen, the block is simply skipped. The success
# screen below is the real assertion.
- runFlow:
when:
visible:
id: "pay-loading-setup-note"
commands:
- takeScreenshot: usdt-permit2-approve-setup

# Wait for the success screen. Generous timeout: two on-chain txs on Polygon.
- extendedWaitUntil:
visible:
id: "pay-result-success-icon"
timeout: 90000

- tapOn:
id: "pay-button-result-action-success"

# Verify the payment modal is dismissed and we're back at the main wallet screen
- extendedWaitUntil:
visible:
id: "button-scan"
timeout: 10000

- stopRecording
34 changes: 33 additions & 1 deletion maestro/pay-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ Every wallet platform must add these accessibility identifiers to the correspond
|---|---|---|
| `pay-merchant-info` | Merchant display | Shows merchant name and payment amount |
| `pay-loading-message` | Loading text | Shown during payment processing |
| `pay-loading-setup-note` | Token setup note | Secondary line shown only while setting up a token for the first time (e.g. the USDT Permit2 approve step) |

### Payment Modal — Option Selection

| TestID | Element | Description |
|---|---|---|
| `pay-option-{index}` | Payment option (unselected) | 0-based index from the payment options array |
| `pay-option-{index}-selected` | Payment option (selected) | Same element when selected |
| `pay-option-{assetSymbol}-{networkName}` | Payment option (stable) | Lowercased, spaces → `-` (e.g. `pay-option-usdt-polygon`). Additive to the index-based id; lets a flow pick a specific asset+network when the same token appears on multiple networks. Used by `pay_usdt_polygon`. |
| `pay-info-required-badge` | "Info required" badge | Shown on options that require KYC |
| `pay-button-info` | Info (?) button in header | Explains KYC requirement |
| `pay-button-continue` | Continue button | Proceeds after selecting a payment option |
Expand Down Expand Up @@ -155,10 +157,40 @@ Each merchant pair represents a different test configuration. The tests use thes
| `pay_kyc_back_navigation` | Back/close button navigation in KYC | MULTI_KYC |
| `pay_insufficient_funds` | Payment amount exceeds wallet balance | SINGLE_NOKYC |
| `pay_double_scan` | Re-scan same QR after completion | SINGLE_NOKYC |
| `pay_usdt_polygon` | USDT on Polygon — Permit2 token: wallet sends `approve` then `pay` (two-tx path) | MULTI_NOKYC |
| `pay_expired_link` | Hardcoded expired payment URL | None (hardcoded) |
| `pay_cancelled` | Hardcoded cancelled payment URL | None (hardcoded) |

All flows are tagged with `pay` for filtering via `--include-tags`.
All flows are tagged with `pay` for filtering via `--include-tags`. `pay_usdt_polygon` additionally
carries the `pay-usdt-polygon` tag so it can be run on its own.

### Permit2 tokens & the allowance reset (`pay_usdt_polygon`)

USDT on Polygon is a plain ERC-20 (no EIP-3009/2612), so WC Pay pays it via
[Permit2](https://github.com/Uniswap/permit2): the wallet sends an `approve` (allowance) transaction
**and then** the payment transaction. The flow asserts the success screen and best-effort observes the
approve step via the `pay-loading-setup-note` testID.

> Polygon is used deliberately: USDT on **Arbitrum** is EIP-3009 (signature-based / gasless), so WC Pay
> never returns an on-chain `approve` action there and the approve step would never run.

To keep the `approve` step exercised on every run, the consuming repo must **reset the Permit2
allowance back to 0 after the test**. This is a real Node script that signs a transaction, so it can
**not** run inside Maestro's `runScript` sandbox (GraalJS — no `require`, no signing). It lives in a
sibling CI-helper dir, `scripts/revoke-permit2-approval.js` (deps pinned in `scripts/package.json`),
and runs as a post-test Node step:

```bash
cd maestro/pay-tests/scripts && npm ci
node revoke-permit2-approval.js \
--chainId eip155:137 \
--privateKey "$TEST_WALLET_PRIVATE_KEY" \
--rpcUrl https://polygon-bor-rpc.publicnode.com
# --walletAddress is optional: derived from the key when omitted; verified to match when passed.
# Polygon (eip155:137) min priority fee defaults to 25 gwei in the script.
```

The test wallet must hold USDT **and** a little POL (gas) on Polygon.

## Deep Link Support

Expand Down
12 changes: 12 additions & 0 deletions maestro/pay-tests/scripts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "wc-pay-maestro-scripts",
"version": "1.0.0",
"private": true,
"description": "CI helper scripts for the shared WalletConnect Pay Maestro tests. Unlike .maestro/scripts/*.js (which run in Maestro's runScript sandbox), these are real Node scripts that may sign transactions and require dependencies.",
"scripts": {
"permit2:revoke": "node ./revoke-permit2-approval.js"
},
"dependencies": {
"ethers": "5.8.0"
}
}
Loading