Investigate gas limit risk: set_discount_registry_all emits unbounded submessages #123
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#123
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Report from code review / audit.
File:
smartcontracts/contracts/factory/src/contract.rsLocation:
execute_set_discount_registry_all(currently ~L335–L364; report cited L335)Behavior
The handler loads
PAIR_COUNTand loops0..count, pushing aWasmMsg::Execute(SetDiscountRegistry) for each registered pair into a singleResponseviaadd_messages.At large numbers of pairs, the serialized response / submessage vector can become large enough to hit chain gas limits (or related limits), making governance unable to roll out discount registry updates across the full set in one transaction.
Suggested direction
Add pagination (or an equivalent chunked workflow), e.g. a new execute path such as
execute_set_discount_registry_batch { start_after, limit }(naming TBD) that processes a bounded slice ofPAIR_INDEXper call, with callers / tooling repeating until complete.Acceptance criteria (proposal)
/label ~bug ~smart-contract(adjust labels as needed for this project)Implementation merged to main (
187bff4): FactorySetDiscountRegistryBatchpaginatesPAIR_INDEXscans withstart_after(exclusive numeric cursor) +limit(dex_common::pagination, default 10, max 30 Wasm executes per tx). Response attributes:pairs_updated,has_more, optionalnext_start_after,scanned_through_index.SetDiscountRegistryAllunchanged;pairs_updatednow reflects actual message count.Docs / crosslinks:
docs/contracts-terraclassic.md(rollout invariants +#factory-discount-registry-rollout-invariants-glab-123),docs/contracts-security-audit.md,docs/deployment-guide.md,docs/testing.md,smartcontracts/scripts/deploy.sh,skills/AGENTS_TERRACLASSIC_GAS.md,docs/README.md.Tests:
factory_coverage_tests::test_set_discount_registry_batch_*,cargo testgreen.@brouie Please verify against the checklist below (issue stays open until you sign off).
Verification checklist
ExecuteMsgJSON:set_discount_registry_batchacceptsregistry, optionalstart_after, optionallimit.limit=1on a factory with ≥2 pairs emitshas_more=trueandnext_start_afterconsistent with continued scanning.start_after= that cursor completes withhas_more=falseand nonext_start_after.start_afterbeyond last index returnspairs_updated=0,has_more=false(idempotent).Unauthorized.min(limit cap, remaining pairs)discount-registry Wasm messages (consistent withpagination::MAX_LIMIT).Nonewhen clearing.mentioned in issue #133
mentioned in issue #121
@PlasticDigits — source-side verification PASS on the SetDiscountRegistryBatch pagination.
ran the new test trio:
these map cleanly to your checklist — paginates_cursor covers the limit=1 + has_more + next_start_after path, noop_when_cursor_past_end covers idempotent past-end, unauthorized covers the non-governance reject. full workspace 321/321 PASS.
source review:
contract.rs:94-98:SetDiscountRegistryBatch { registry, start_after, limit }accepted, all three params optional exceptregistrycontract.rs:391(execute_set_discount_registry_batch)contract.rs:408-411emitspairs_updated=0+has_more=false(nonext_start_afterattribute) — matches idempotent semanticscontract.rs:430-431:has_more = idx < countafter the scan completesnext_start_after = has_more.then_some(idx.saturating_sub(1))contract.rs:437-443:action,pairs_updated,has_more, optionalnext_start_afterSetDiscountRegistryAllpreserved separately —pairs_updatedattribute now reflects actual message count rather than the static countdefaults / caps come from
dex_common::paginationper your description (default 10, max 30) — not inlined in factory contract, which is the right place for the constant.still pending: live ops verification — multi-tx rollout against a real factory with N>30 pairs, observing that each tx carries at most min(limit, remaining) wasm executes, and that final batch returns
has_more=falsecleanly. queued behind the other live-walk debt.ready for close on your side once you are happy with source PASS, or hold for chain-ops walk.