Investigate: Factory PENDING_PAIR race — wrong assets may be registered #121
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#121
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?
Summary
Investigate reported behavior in the factory contract around
PENDING_PAIR,execute_create_pair, andreply_instantiate_pair: a single global pending slot combined with unusedMessageInfomay cause incorrect registry entries and unintended permissionless pair creation.Reported severity
High
Location
smartcontracts/contracts/factory/src/contract.rsexecute_create_pair: lines ~119–198reply_instantiate_pair: lines ~596–618Code behavior (as reported)
execute_create_pairtakes_info: MessageInfounused (permissionless callers). It saves pending assets:reply_instantiate_pairloads from that slot and registers the pair:Issue A — Permissionless pair creation
MessageInfois explicitly ignored; any wallet could callCreatePairfor whitelisted-code-ID tokens, bypassing governance intent if governance-gated creation was assumed. Risks cited: spam/unwanted pairs, squatting on key pairs, subtle token-ordering traps. Report notes permissionless AMM creation is sometimes intentional; paired with Issue B this becomes more serious.Issue B — Single-slot
PENDING_PAIRraceCosmWasm executes one tx at a time per contract, but two
CreatePairtxs in the same block from different senders could run sequentially: the second overwritesPENDING_PAIRbefore the firstreply_instantiate_pairruns, so the first reply may read the second caller’sasset_infosand register wrong assets for the first pair’s address. Impact: factory registry disagrees with the pair contract’s real token setup → incorrect routing → potential fund loss.Impact (as reported)
asset_infosfor a new pair → incorrect routing → fund lossRecommended fix (from report)
Race: Replace
Item<[AssetInfo; 2]>with something keyed uniquely, e.g.Map<u64, [AssetInfo; 2]>by reply ID, orMap<Addr, [AssetInfo; 2]>keyed by instantiating contract address from the reply; use a unique reply ID per pending pair where appropriate.Permissionless: If governance-gated creation is desired, add
ensure_governancetoexecute_create_pair. If permissionless is intentional, document it and still fix Issue B.Suggested test (from report)
Next steps
CreatePairis intended to be permissionless for this DEX.cw-multi-testor integration tests.mentioned in commit
256763ba88Fix merged to
main(please verify) — @brouieSummary
CreatePairpreserved (no governance gate on pair creation).CreatePairthat enters the pending/instantiate path per block height (PAIR_CREATION_BLOCK+ContractError::OnePairCreationPerBlock).WasmMsg::Instantiatesubmessages +reply) finishes before the next tx runs, so the second transaction overwritingPENDING_PAIRbefore the first tx’s reply scenario does not apply on-chain. The per-block gate is still defense-in-depth + explicit rate limit and matches the desired product constraint.Code / docs
smartcontracts/contracts/factory/src/{contract,state,error,lib}.rsfactory_tests::test_create_pair_one_per_block_then_next_block_ok(+ test helpers advance block between multiple creates).docs/security-model.md(new subsection),docs/contracts-terraclassic.md,docs/contracts-security-audit.md(F1).skills/AGENTS_LOCALNET_TRADING_SWARM.md(rule 6: onecreate_pairper block in automation).Verification checklist for @brouie
create_pairtxs in the same block (or from the same client without waiting for height): second should fail with Only one CreatePair may run per block; retry next block.create_pairwhen whitelist rules pass.docs/security-model.md#createpair-rate-limit-and-pending-stateand confirm wording matches your understanding of atomicity vs the per-block gate.Issue left open for your sign-off.
@brouie Update: at merges **** on top of the #121 per-block gate, so imports both and . Factory CONTRACT_VERSION is 1.1.0 (122 migrate path). The verification checklist in the previous note still applies.
Update for brouie: main commit
c8f713cmerges the pair address registry work (GitLab 122) on top of the 121 per-block CreatePair gate. Factory imports both PAIR_ADDR_REGISTERED and PAIR_CREATION_BLOCK; contract version is 1.1.0. Prior verification checklist still applies.mentioned in issue #133
@PlasticDigits — source-side verification PASS on the per-block gate. happy with the design rationale on the cross-tx atomicity argument too — gate is good defense-in-depth and matches the desired product constraint regardless.
ran your named test:
full workspace cargo test 321/321 PASS, 0 failures (up from 308/308 baseline at
5c744ee— 13 new tests across #121/#122/#123).source review of the gate:
PAIR_CREATION_BLOCK: Item<u64>declared instate.rs:44OnePairCreationPerBlockerror variant inerror.rs:25contract.rs:190-196insideexecute_create_pair— loads stored block, compares toenv.block.height, errors if same height, then saves current height after passing. positioned before anyPENDING_PAIR.saveso the flow is clean.verified the docs section
docs/security-model.md#createpair-rate-limit-and-pending-statereads correctly — the wording on cosmos-sdk tx atomicity (full execute + submessages + reply finishing before next tx) tracks with how I understand the runtime, and the per-block gate as explicit rate-limit + defense-in-depth makes sense even if the cross-tx race scenario does not apply in practice.agent skill rule 6 in
AGENTS_LOCALNET_TRADING_SWARM.mdcovers swarm scripts that loopcreate_pair— useful guardrail.still pending: live ops verification — submitting two
create_pairtxs in the same block on LocalTerra/Station and observing theOnePairCreationPerBlockrevert on the second, then retry on next block. that is laptop-session work, queued behind #115 and #118 item 4 in the live-walk debt.ready for close on your side once you are happy with source PASS, or hold open if you want me to schedule the chain-ops walk first.