-
Notifications
You must be signed in to change notification settings - Fork 14
feat: Manteca entity deposit addresses from the API (TASK-22107) #2933
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: dev
Are you sure you want to change the base?
Changes from 1 commit
33cb733
09199fa
b45de3d
d71c473
7da06ad
a36aa99
50e7e3d
d8798af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -753,9 +753,14 @@ export default function QRPayPage() { | |
| const requiredUsdcAmount = parseUnits(finalPaymentLock.paymentAgainstAmount, PEANUT_WALLET_TOKEN_DECIMALS) | ||
| signedArtifact = await signSpend({ | ||
| requiredUsdcAmount, | ||
| // Per-rail Manteca QR funding wallet: Pix → non-AR, everything else → AR | ||
| // (same binary heuristic as the backend's getQrReceiveAddress). | ||
| recipient: qrType === EQrType.PIX ? MANTECA_QR_DEPOSIT_ADDRESS_NON_AR : MANTECA_QR_DEPOSIT_ADDRESS_AR, | ||
| // Entity-aware deposit address served by the API (per-entity | ||
| // balances from 2026-09-14) — the backend resolves the entity | ||
| // from the QR and the paying Manteca account. The per-rail | ||
| // constants remain only as a fallback for an older API that | ||
| // does not return the field yet. | ||
| recipient: | ||
| (finalPaymentLock.depositAddress as `0x${string}` | undefined) ?? | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR: [claude-opus] Manteca spend recipient now comes from the API with no test on either page This diff changes WHERE user USDC is sent in two additional flows and ships no test for either. src/app/(mobile-ui)/qr-pay/page.tsx:762 now prefers Evidence that nothing covers it: Untested cases to name: (1) qr-pay, lock returns depositAddress → signSpend called with that address, NOT the PIX/non-PIX constant (assert on |
||
| (qrType === EQrType.PIX ? MANTECA_QR_DEPOSIT_ADDRESS_NON_AR : MANTECA_QR_DEPOSIT_ADDRESS_AR), | ||
| rainSpendingPower: rainCentsToUsdcUnits(rainCardOverview?.balance?.spendingPower), | ||
| kind: 'QR_PAY', | ||
| }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,9 +62,27 @@ const MantecaReviewStep: FC<MantecaReviewStepProps> = ({ | |
| setError(null) | ||
| setIsSubmitting(true) | ||
|
|
||
| // Entity-aware deposit address (per-entity balances from | ||
| // 2026-09-14): ask /withdraw/init where THIS currency's | ||
| // offramp must be funded BEFORE spending the one-shot claim | ||
| // link. If init fails outright, abort — no funds have moved | ||
| // and the user can retry; claiming to a hardcoded address and | ||
| // then failing would strand the link's funds at the wrong | ||
| // entity. The constant remains only for an older API that | ||
| // does not return the field yet. | ||
| let depositAddress: string = MANTECA_DEPOSIT_ADDRESS | ||
| const { data: initData, error: initError } = await mantecaApi.initiateWithdraw({ amount, currency }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR: Add coverage for the pre-claim entity lookup This is the safety boundary that keeps a one-shot link from funding the wrong Manteca entity, but no test renders MantecaReviewStep or exercises this branch. A later refactor could ignore initData.depositAddress or let claimLinkSecure run after an init error, stranding a BRL link after the entity cutoff without any suite failure. Add component tests that assert the API-served address is passed to claimLinkSecure and that an init error calls neither claimLinkSecure nor withdraw. The QR-pay and bank-withdraw signSpend recipient selections should likewise be pinned because they move funds.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR: [claude-opus] Claim-link offramp fails closed on a field the merged API does not serve — hard deploy-order dependency on the peanut-api-ts half
Unlike qr-pay and the bank-withdraw page — where the author deliberately kept a constant fallback, so those degrade safely — this path has no fallback by design, which is the right safety call but makes the FE unshippable ahead of the API. Second, coupled evidence in the same direction: once the API does serve an entity address, a BRL claim will be claimed to the CRYPTO_GLOBAL address, while the merged legacy withdraw route still validates the funding transfer against This is almost certainly the paired half in the open peanut-api-ts#1487 (its
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR: [claude-opus] Claim-link offramp fails closed on a field the merged API does not serve
|
||
| if (initError) { | ||
| setError(t('manteca.errors.generic')) | ||
| return | ||
| } | ||
| if (initData?.depositAddress) { | ||
| depositAddress = initData.depositAddress | ||
| } | ||
|
|
||
| // Use secure SDK claim (password stays client-side, only signature sent to backend) | ||
| const txHash = await claimLinkSecure({ | ||
| address: MANTECA_DEPOSIT_ADDRESS, | ||
| address: depositAddress, | ||
| link: claimLink, | ||
| }) | ||
|
|
||
|
|
||
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.
MAJOR: [moonshotai/kimi-k3] API-served depositAddress used without runtime validation before signing fund transfers
qr-pay (and identically withdraw/manteca at
recipient: (priceLock.depositAddress as0x${string}| undefined) ?? MANTECA_DEPOSIT_ADDRESS) now takes the recipient for an irreversible spend from the/manteca/qr-payment/init(or/withdraw/init) response. The only 'check' is a TypeScriptas0x${string}`` cast, which is a compile-time assertion and validates nothing at runtime. Two concrete failure modes: (1)??only falls back on null/undefined, so an API that returns `depositAddress: ""` (or any non-null malformed value) bypasses the constant fallback and is signed to as the recipient — MantecaReviewStep correctly guards truthiness with `if (initData?.depositAddress)`, but qr-pay and withdraw do not; (2) a validly formatted but wrong address returned by an API bug, stale lock, or manipulated response is now signed to directly, whereas previously the recipient was a compile-time constant. The PR's own reasoning for the claim-link flow (wrong recipient strands funds; the QA note about a server-side 400 at complete time does not recover funds already on-chain) applies equally to signSpend here. Fix: before using the served value, run a runtime check — e.g. `viem`'s `isAddress(depositAddress)` (and non-empty) — and fall back to the constant with the `[manteca-entity-legacy-funding]` log (or abort) when it fails; ideally compare against the small static set of known legal-entity addresses rather than trusting an arbitrary address from the wire.