-
Notifications
You must be signed in to change notification settings - Fork 271
Sam/ramp plugins #5691
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Sam/ramp plugins #5691
Conversation
This is an attempt to get the agent to automatically document lessons learned to the docs/ directory. This way conventions are maintained and over time as the agent is course corrected.
- Create docs/localization-guidelines.md with mandatory UI string localization rules - Create docs/component-styling-guidelines.md with styled HOC usage patterns - Update AGENTS.md with Documentation section indexing all docs/ files - Add rule requiring all docs/ markdown files to be indexed in AGENTS.md - Add localized strings for TradeRegionSelectScene to en_US.ts
f3d85d5
to
f59d0c0
Compare
1620f45
to
3844949
Compare
AGENTS.md
Outdated
@@ -1,5 +1,16 @@ | |||
# Edge React GUI - Agent Guidelines | |||
|
|||
> **⚠️ IMPORTANT: Learning and Documentation** | |||
> | |||
> **ALWAYS search for relevant documentation first** before starting any task that lacks sufficient context: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"before starting any task that lacks sufficient context:"
This will likely be a noop. Agents (at least in my experience) can rarely tell whether they have sufficient information. This sentence just adds extra thinking time.
I see two distinct sections here that make this a cleaner thinking process
# Initialization
**ALWAYS ensure `docs/` is in context before beginning a task. If not, **Use `find docs/ -name "*.md" -type f`** to recursively list all `.md` files in `docs/` folder to get an index of available documentation, then **Read relevant docs** to understand existing conventions, patterns, and business logic.
# Workflow
...rest of the items
src/actions/ExchangeRateActions.ts
Outdated
@@ -359,3 +359,117 @@ export const closestRateForTimestamp = ( | |||
} | |||
return bestRate | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just do a single call to getHistoricalRate but using the current date. This removes all this code
const checkAssetSupport = ( | ||
direction: FiatDirection, | ||
fiatCurrencyCode: string, | ||
cryptoPluginId: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just access an EdgeAsset
instead of separate pluginId,tokenId
// Check if crypto is supported | ||
const paybisCc = | ||
EDGE_TO_PAYBIS_CURRENCY_MAP[`${cryptoPluginId}_${tokenId ?? ''}`] | ||
if (!paybisCc) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did ai write this? need explicit null check
request: RampQuoteRequest | ||
): Promise<RampQuoteResult[]> => { | ||
await ensureStateInitialized() | ||
if (!state) throw new Error('Plugin state not initialized') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did AI write this? need explicit null check
d39e2e2
to
ec70370
Compare
amountType: lastUsedInput, | ||
direction: 'buy', | ||
regionCode: { | ||
countryCode: countryCode || 'US', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
countryCode ?? US
|
||
// Calculate exchange rate from best quote | ||
const quoteExchangeRate = React.useMemo(() => { | ||
if (!bestQuote?.cryptoAmount || !bestQuote.fiatAmount) return 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (bestQuote?.cryptoAmount == null || bestQuote.fiatAmount == null)
headerTitle={lstrings.buy_crypto} | ||
headerTitleChildren={ | ||
<PillButton | ||
icon={() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
icon={renderRegionIcon}
|
||
interface Props extends BuyTabSceneProps<'pluginListBuy'> {} | ||
|
||
export const TradeCreateScene = (props: Props): React.ReactElement => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const TradeCreateScene: React.FC<Props> = (props: Props) => {
displayName: selectedQuote.pluginDisplayName, | ||
icon: { uri: selectedQuote.partnerIcon } | ||
}} | ||
renderRight={isBestRate ? () => <BestRateBadge /> : undefined} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renderRight={renderBestRate}
icon: { uri: selectedQuote.partnerIcon } | ||
}} | ||
renderRight={isBestRate ? () => <BestRateBadge /> : undefined} | ||
onPress={async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onPress={handlePress}
|
||
interface Props extends BuyTabSceneProps<'rampSelectOption'> {} | ||
|
||
export const TradeOptionSelectScene = (props: Props): React.JSX.Element => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const TradeOptionSelectScene: React.FC<Props> = (props: Props) => {
onProviderPress: () => Promise<void> | void | ||
} | ||
|
||
export const PaymentOptionCard = (props: Props): React.JSX.Element => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const PaymentOptionCard: React.FC<Props> = (props: Props) => {
disabled?: boolean | ||
} | ||
|
||
export const PillButton = (props: PillButtonProps): React.ReactElement => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const PillButton: React.FC<PillButtonProps> = (props: PillButtonProps) => {
testID?: string | ||
} | ||
|
||
export const DropDownInputButton = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const DropDownInputButton: React.FC<DropDownInputButtonProps> = (props: DropDownInputButtonProps) => {
import { EdgeText } from '../themed/EdgeText' | ||
|
||
// TODO: Render a badge icon | ||
export const BestRateBadge = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const BestRateBadge: React.FC = () => {
src/hooks/useSupportedPlugins.ts
Outdated
.filter(check => check.supported) | ||
.map(check => check.plugin) | ||
}, | ||
enabled: Boolean( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enabled: (selectedWallet != null && selectedCrypto != null && selectedFiatCurrencyCode != null && regionCode != null),
src/actions/DeepLinkingActions.tsx
Outdated
fiatProviderDeeplinkHandler(link) | ||
// Try ramp deeplink handler first | ||
try { | ||
rampDeeplinkManager.handleDeeplink(link) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const handled = rampDeeplinkManager.handleDeeplink(link) if (!handled) fiatProviderDeeplinkHandler(link)
this.listener = null | ||
} | ||
|
||
handleDeeplink(link: FiatProviderLink): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleDeeplink(link: FiatProviderLink): boolean {
if (this.listener == null) return false
const { direction, providerId, deeplinkHandler } = this.listener
if (link.providerId !== providerId) return false
if (link.direction !== direction) return false
if (Platform.OS === 'ios') SafariView.dismiss()
this.unregister()
deeplinkHandler(link)
return true
}
} | ||
} | ||
|
||
const privateKey = atob(state!.privateKeyB64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const privateKey = Buffer.from(state!.privateKeyB64, 'base64').toString('binary')
|
||
{/* Bottom Input (Crypto by design) */} | ||
<InputRow> | ||
<DropDownInputButton onPress={handleCryptDropdown}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid empty uri image source.
{selectedCrypto == null || selectedWallet == null ? null : ( <CryptoIcon sizeRem={1.5} pluginId={selectedWallet.currencyInfo.pluginId} tokenId={selectedCrypto.tokenId} /> )}
This file contains run configuration such as RN_SIMULATOR and RN_PORT to be used by a rn-ios script. This is for development purposes
This is a consistent card design for the new fiat buy UI.
- Implement TradeCreateScene and TradeOptionSelectScene for buy/sell flow - Add Paybis as first ramp plugin with full API integration - Create reusable hooks for ramp plugin management (useRampPlugins, useRampQuotes) - Add payment type icon system with comprehensive mappings - Implement quote fetching and comparison across multiple providers - Add best rate badge component for quote comparison - Create ramp plugin type definitions and store utilities - Add comprehensive documentation for migration and architecture - Include unit tests for payment types and store IDs - Update navigation and deeplink handlers for ramp flows BREAKING CHANGE: Replaces legacy FiatPluginUi with new ramp plugin system
fixup! Add learning technique to AGENTS.md Restructure into Initialization and Workflow sections for clarity
ec70370
to
c177303
Compare
docs/ramp-plugin-migration-guide.md
Outdated
**Before:** | ||
```typescript | ||
const assets = await provider.getSupportedAssets({ | ||
direction: 'buy', | ||
paymentTypes: ['credit', 'bank'], | ||
regionCode: { countryCode: 'US', stateCode: 'CA' } | ||
}) | ||
``` | ||
|
||
**After:** | ||
```typescript | ||
const assets = await plugin.getSupportedAssets({ | ||
direction: 'buy', | ||
paymentTypes: ['credit', 'bank'], | ||
regionCode: { countryCode: 'US', stateCode: 'CA' } | ||
}) | ||
``` | ||
|
||
The API remains the same, maintaining compatibility with existing code. Note that the method returns the asset map for the first supported payment type from the provided array. | ||
|
||
## Removing getSupportedAssets | ||
|
||
The ramp plugin architecture has been simplified by removing the separate `getSupportedAssets` method. All support checking is now done within the `fetchQuote` method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like conflicting information around getSupportedAssets
src/hooks/useSupportedPlugins.ts
Outdated
}: UseSupportedPluginsParams): UseSupportedPluginsResult => { | ||
// Build region code | ||
const regionCode: FiatPluginRegionCode | undefined = React.useMemo(() => { | ||
if (!countryCode) return undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
countryCode == null
|
||
return { | ||
supportedPlugins, | ||
isLoading, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't seem to be doing anything with isLoading
? Should be true while we're loading
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in the TradeCreateScene
const rateA = parseFloat(a.fiatAmount) / parseFloat(a.cryptoAmount) | ||
const rateB = parseFloat(b.fiatAmount) / parseFloat(b.cryptoAmount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No guards against 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not addressed
"Fix all non deprecation lint warnings in all changed files" Useful rule: |
|
||
try { | ||
return ( | ||
parseFloat(bestQuote.fiatAmount) / parseFloat(bestQuote.cryptoAmount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No guard for div by 0
Remove silent-but-deadly try-catch errors.
Improve rampDeeplinkManager
Add URLs to paybis config
Fix KavButton usage
This was a legit bug for both the ramp plugin and the paybis provider. The failure callback URL includes the param set to "fail" not "failure".
const rateA = parseFloat(a.fiatAmount) / parseFloat(a.cryptoAmount) | ||
const rateB = parseFloat(b.fiatAmount) / parseFloat(b.cryptoAmount) | ||
return rateA - rateB | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Zero Crypto Amount Causes Quote Sorting Failure
The quote sorting logic in useRampQuotes
and TradeOptionSelectScene
divides by cryptoAmount
without handling cases where it's zero or invalid. This can produce NaN
or Infinity
rates, breaking the sort comparison and leading to incorrect quote ordering.
Additional Locations (1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not addressed here and src/components/scenes/TradeOptionSelectScene.tsx#L121-L127
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all divide by 0 comments are addressed
const rateA = parseFloat(a.fiatAmount) / parseFloat(a.cryptoAmount) | ||
const rateB = parseFloat(b.fiatAmount) / parseFloat(b.cryptoAmount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not addressed
CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
noneRequirements
If you have made any visual changes to the GUI. Make sure you have: