RWAs on the engine
For a pool we create, the engine manages pooled capital and our hook enforces permissions and an appraisal-based trading band.
Testnet demonstration on Base Sepolia, with a fictional asset and valueless test tokens. Uniswap’s own contracts are audited; Mintware’s composition with them is unaudited.
The standard for markets with holder rules.
Shared market and compliance plumbing.
The issuer supplies the asset, holder rules and dated appraisal.
This role is a test fixture today.
The shared MarketEngine runs each asset’s market.
Our hook enforces appraisal and permission rules.
The app brings the unit into view.
The current property unit uses MintwareOwnedPoolMarketEngine on Base Sepolia. The deployment record includes activation, seed contribution and liquidity deployment. It replaces the retired standalone property demo.
Underlying tokens outside; virtual tokens inside
The pool’s currencies are the verified PermissionsAdapter and USDC. This Uniswap wrapper holds the restricted underlying property token and creates a 1:1 virtual currency that only PoolManager holds. When PoolManager takes an output, the wrapper burns the virtual units and delivers the underlying token to the recipient. See Uniswap’s Permissioned Pools overview for the underlying standard.
Pool currencies are sorted by their addresses. The engine replaces the adapter currency with its underlying token in that same slot, without sorting again. Appraisals, NAV, deposits and payouts preserve this correspondence. Always read the engine’s token order.
Three permission checks, not one allowlist
| Boundary | What must be allowed |
|---|---|
| Pool activity flags | The issuer checker supplies SWAP_ALLOWED and LIQUIDITY_ALLOWED independently. A trader needs the swap flag; engine and share recipient need the liquidity flag for new shares. |
| Trusted wrappers | The adapter owner approves wrappers with allowedWrappers. The hook checks trust before calling msgSender() to obtain the represented principal. The owned engine reports itself, not the person calling recenter. |
| Native token custody | The issuer token’s own transfer restrictions still apply to contributors, recipients, the PermissionsAdapter, the engine and the property idle adapter. Pool flags do not override native transfer rules. |
Only the bound engine may add or remove liquidity through this hook. Adds check wrapper trust and the engine’s liquidity flag. Removals and fee collection do not check current allowlist permission, wrapper approval, trading pause, swap enablement or appraisal freshness. Underlying transfers must still succeed.
Owned-pool shares cannot transfer between nonzero addresses: both transfer and transferFrom hit SharesNonTransferable. This prevents passing an existing pooled claim around the eligibility checks enforced on mint. It does not add forced seizure or recovery. Burns remain possible for exits.
The appraisal is also the engine’s mark
MintwareRwaAppraisalHook implements IMarketAssetMark. A publication includes an appraisal tick, the time the underlying observation was made, and a report hash. Zero or future observation times, non-increasing observation times and an empty report hash are rejected. The report hash is emitted in ObservationRecorded; the contract does not verify the appraisal’s real-world truth.
The first appraisal may be initialized by the owner or keeper. Later postAppraisal(int24 tick, uint64 observation, bytes32 reportHash) calls are keeper-only. Three constraints limit updates:
maxStepTicksbounds each absolute tick change.minUpdateIntervalspaces publications using their on-chain publication time.maxDriftTicksPerDaycaps the sum of absolute movements in the rolling 24 hours. Reversals count too; each movement expires separately.
Freshness uses the observation time, not publication time. Read config() for the actual band, fee, cadence, drift, age and grace parameters. The configuration fields are policy limits for this hook; they are not a promised valuation range for the asset.
Trading band and the gap-closing exception
Before a swap, the hook requires a bound engine, enabled adapter swap switch, approved wrapper and permitted trader, an unpaused hook, and a fresh appraisal. It selects coreFeePips or specFeePips from the pre-swap distance to the appraisal tick.
Accept if the ending tick is strictly nearer the appraisal and stays on the same side as the starting tick.
Revert the swap, including a move that crosses the appraisal and ends beyond the opposite outer boundary.
This exception lets trades reduce a gap after an appraisal moves. It does not permit arbitrary trading outside the outer band. The hook’s appraisal band is distinct from the engine’s LP range; the engine independently applies its pool-versus-mark guard when deploying or recentering.
Keeper rotation and configuration
Replacing the keeper requires owner proposal and confirmation after 48 hours. Configuration can change immediately before pool initialization; after the pool is bound, changes use a 48-hour proposal/confirmation delay. Appraisal maximum age and exit grace are frozen once the pool key is pinned. The owner can replace the guardian immediately.
The hook owner or guardian can call pauseTrading(); only the owner calls resumeTrading(). This is independent of the engine’s pause. The adapter owner separately controls the checker, trusted wrappers and swap switch. Those adapter controls do not inherit the hook’s timelock.
Stale appraisal: trading halts, exits get a grace window
| Appraisal state | Trading and new capital | Exits |
|---|---|---|
| Fresh | Subject to permissions, pause, switch and engine checks. | Full delivery or partial delivery with a usable exit mark and the applicable guards. |
| Stale, within exit grace | Hook swaps halt. Engine fresh-price reads fail, blocking deposits, deployment and recenter. | The exit mark can still value an incomplete delivery. Full in-kind delivery does not need a mark. |
| Beyond exit grace | Trading remains halted until a valid fresh appraisal is published. | Complete pro-rata in-kind delivery can still succeed. Incomplete delivery requiring a mark reverts. |
Permission revocation does not by itself gate pool removal or collection. However, issuer freezes, blocked recipients, unreadable adapter NAV or failed fee collection can still prevent a withdrawal. A recenter that cannot re-add liquidity reverts atomically, restoring the original position.
What is not enabled yet
The committed deployment record says swaps are disabled: the adapter swap switch is off and no router is allowed. Trading needs a permissioned router approved in allowedWrappers, its trader principal allowed to swap, the switch enabled, and all hook and native transfer checks satisfied. The owned engine’s optional idle-swap path is deliberately disabled.
The asset and allowlist are test-only fixtures with open setters, not production issuer controls. The app’s RWA unit and public read endpoint expose current reads separately from these recorded deployment facts.
| Contract | Explorer |
|---|---|
| MarketEngine | 0xf47c43eCb582cD05D5CDCEc7f8949b2fc437198c |
| Appraisal / permission hook | 0x21094805d6176cC47E76C94d266D274E5439EAC0 |
| Permissions adapter | 0x256b86557F21fcFbF1ddd36713C2883fCd2c6390 |
| Permissions adapter factory | 0xBE3367b32Ef35FB51400f7afe6eC7B93b1ed61a1 |
See all current deployments, including the issuer fixtures and idle adapters.
Source reference
Behavior above follows these files in the Mintware repository. Deployment records describe recorded state, not a fresh chain read.
contracts-v4/src/vaults/MintwareOwnedPoolMarketEngine.solcontracts-v4/src/rwa/MintwareRwaAppraisalHook.solcontracts-v4/src/lib/MWV4Settlement.solcontracts-v4/lib/v4-periphery-permissioned/src/hooks/permissionedPools/PermissionsAdapter.solcontracts-v4/lib/v4-periphery-permissioned/src/hooks/permissionedPools/libraries/PermissionFlags.sollib/rwa/units.tsconfig/deployments.json