Best Practices
Last modified:
Best practices for integrating with the CenturionDEX Protocol Fee system.
Calldata nonce
Firepit.release() requires a _nonce parameter that matches the on-chain Firepit.nonce().
The nonce enforces sequencing to prevent front-running. Without it, an attacker could front-run a release() call and cause the victim to burn CRX in exchange for little or no assets. Because two competing transactions that share a nonce will produce at most one success, the caller must read the nonce off-chain and pass it in as calldata.
Integrating contracts should NEVER read the nonce at the time of the transaction
contract Vulnerable {
IFirepit public firepit;
function vulnerable(Currency[] memory assets) external {
// ❌ THIS IS VULNERABLE TO FRONT RUNNING ❌ //
uint256 nonce = firepit.nonce();
firepit.release(nonce, assets, address(this));
}
}contract SafeRelease {
IFirepit public firepit;
// ✅ THIS IS SAFE FROM FRONT RUNNING ✅ //
function safeRelease(uint256 _nonce, Currency[] memory assets) external {
firepit.release(_nonce, assets, address(this));
}
}In the safe pattern, the caller reads
Firepit.nonce()off-chain, associates it with the releasable assets, and passes it as a calldata parameter.
Collect CenturionDEX v3 fees
CenturionDEX v3 fees must be collected to the TokenJar before they become eligible for release. See Read Asset Balance for details.
Collect outstanding v3 fees before calling Firepit.release() to maximise the assets released in a single transaction.
Collection is available via IV3FeeAdapter.collect().
IV3FeeAdapter v3FeeAdapter;
function collectAndRelease(
IV3FeeAdapter.CollectParams[] memory collectParams,
uint256 _nonce,
Currency[] memory assets,
address recipient
) external {
v3FeeAdapter.collect(collectParams);
firepit.release(_nonce, assets, recipient);
}CRX approvals
The Firepit.threshold() is updatable. While the system is not designed to front-run .release() calls, a max-approval of CRX could lead to a larger-than-expected burn if the thresholdSetter raises the threshold while a .release() transaction is pending.
Mitigations:
- Hold only the amount of CRX you are willing to burn.
- Approve only the amount you intend to burn in the current call.
- Check balances before and after
.release(), comparing the actual burn against a calldata-supplied expectation.
Payable contracts
The Protocol Fee system can release native tokens (CTN), so recipient contracts must be payable.
Pricing CenturionDEX v2 LP tokens
CenturionDEX v2 protocol fees are automatically pushed to the TokenJar contract as ERC-20 LP tokens, each representing a share of the pool's token0 and token1 reserves. See Read Asset Balance for background.
To compute the underlying amounts represented by a given LP token balance:
ICenturionV2Pair pair;
function getUnderlyingAmounts(uint256 lpTokens) external view returns (uint256 amount0, uint256 amount1) {
uint256 totalSupply = pair.totalSupply();
uint256 poolBalance0 = IERC20(pair.token0()).balanceOf(address(pair));
uint256 poolBalance1 = IERC20(pair.token1()).balanceOf(address(pair));
amount0 = (lpTokens * poolBalance0) / totalSupply;
amount1 = (lpTokens * poolBalance1) / totalSupply;
}Integrators should perform additional validation — pool-price and slippage checks — to avoid misrepresenting the LP token's underlying value.