Skip to content

Account allowances - #1671

Merged
polymesh-bot merged 2 commits into
developfrom
account-allowances
Sep 8, 2026
Merged

polymesh-bot merged 2 commits into
developfrom
account-allowances

Conversation

@F-OBrien

@F-OBrien F-OBrien commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Description

Two commits.

1. List the Asset allowances an Account has approved

account.getAllowances(paginationOpts?): Promise<ResultSet<AssetAllowance>>
fungibleAsset.getAllowances({ owner }): Promise<AssetAllowance[]>
// AssetAllowance = { asset, spender, amount, unlimited }

Only a single allowance could be read before, and only where the spender was already known — which for "what have I approved, and to whom" it is not.

Only the Account-level read pages: allowances are keyed (owner, spender, assetId), so an owner prefix scan is exact, while the Asset-level read can only filter after the scan and would otherwise return short pages behind a cursor still claiming more.

No zero filtering — approve(…, 0) and a spend that drains an allowance both call Allowances::remove, so every result is live.

2. Allow an unlimited allowance to be set

ApproveAllowanceParams.amount documented Balance::MAX = unlimited and the chain honours it, but the value was rejected before it reached the chain — it was converted as a balance, which is capped at MAX_BALANCE. amount and unlimited are now both optional, and exactly one must be passed.

That surfaced a pre-existing bug in bigNumberToU128: it stringified with toString, which switches to exponential notation from 1e21, and the codec rejects that — so no caller could pass a u128 above 1e21, not only this one. Now uses toFixed(). bigNumberToBalance is hardened the same way; it is safe today only because MAX_BALANCE keeps values three orders below the threshold.

Verification

yarn build:ts, yarn lint and yarn test (209 suites / 2738 tests) pass, with 100% coverage on the touched files. api-snapshot:compare reports no breaking changes — run locally, because an earlier discriminated-union form of ApproveAllowanceParams was flagged as breaking (interfacetype reads as removal).

Exercised against a local dev chain, since testnet has one allowance entry and mainnet none:

Check Result
Paged, size: 10 4 pages (10/10/10/1), 31 distinct, no repeats or skips
Balance::MAX set through the SDK, read back with unlimited: true
Asset-level filter 11 of 31, as expected

The u128 bug was only found this way — the unit test mocks bigNumberToU128, so it encoded the same assumption as the code.

Follow-up, not in this PR

fungibleAsset.getAllowance() returns a bare BigNumber with no unlimited, so it disagrees with the new type. Changing it is a return-type break; getAllowances({ owner }) already carries the flag in the meantime.

Breaking Changes

None.

JIRA Link

Checklist

  • Updated the Readme.md (if required) ?

@F-OBrien
F-OBrien requested a review from a team as a code owner September 7, 2026 19:02
`account.getAllowances()` returns every allowance the Account has
approved — the spender, the Asset, and what may be transferred.
`fungibleAsset.getAllowances({ owner })` narrows that to one Asset. Only
a single allowance could be read before, and only where the spender was
already known, which it usually is not.

`unlimited` marks an allowance of `Balance::MAX`, which the chain never
deducts from on spend. Without it a caller has 3.4e32 POLYX to render.

Allowances are keyed by owner, spender and Asset in that order, so the
Account-level read is one prefix scan and pages the chain honestly. The
Asset-level one can only filter after reading, so it returns the whole
set rather than short pages behind a cursor still claiming more.
Revoking an allowance removes its entry, so no zero filtering is needed
and every result is live.
`ApproveAllowanceParams.amount` documented `Balance::MAX = unlimited`
and the chain honours it, but the value never reached the chain: it was
converted as a balance, which is capped at `MAX_BALANCE`. `amount` and
`unlimited` are now both optional and exactly one must be passed.

`bigNumberToU128` was part of the same failure. It stringified with
`toString`, which switches to exponential notation from 1e21 and the
codec rejects — so no caller could pass a `u128` above that, not only
this one. It now spells the digits out.
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedbignumber.js@​9.0.1 ⏵ 9.3.1100100100 +185 -6100

View full report

@F-OBrien
F-OBrien changed the base branch from develop to staking-account-exposure September 8, 2026 14:50
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

Base automatically changed from staking-account-exposure to develop September 8, 2026 15:15
@polymesh-bot
polymesh-bot dismissed prashantasdeveloper’s stale review September 8, 2026 15:15

The base branch was changed.

@F-OBrien

F-OBrien commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

/fast-forward

@polymesh-bot
polymesh-bot merged commit 4557ae9 into develop Sep 8, 2026
17 checks passed
@polymesh-bot
polymesh-bot deleted the account-allowances branch September 8, 2026 15:16
@polymesh-bot

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 31.1.0-beta.12 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants