The Ajna protocol is a non-custodial, peer-to-peer, permissionless lending, borrowing and trading system that requires no governance or external price feeds to function.
Enables usage of mint(pool) and isAjnaPool(pool) usage without requirement of subset hash.
Add internal mapping of known valid subset (and non-subset) pools
Allow for permissionless adding of valid pools to the subset table via a new addAjnaPool function. (Optional for non-subset pools)
Optimize lookups of known Ajna pools within isAjnaPool function .
Automatically add pools to the lookup table upon mint(pool, subsetHash) calls, reducing subsequent mint costs against that pool.
Add lookup of non-subset ERC20 and ERC721 pools (and added ERC721 subset pools) without requiring subset hash.
Add minting of non-subset ERC20 and ERC721 pools (and added ERC721 subset pools) without requiring subset hash.
Gets non-subset hash from the factory as immutable upon PositionManager creation.
Approximate savings of 20k gas per function call once a pool is added.
Description of bug or vulnerability and solution
Description of bug: Usability issue around subset hashes
For subset ERC721 pools, the subset hash is going to be a general barrier for usability as the subsetHash is not available on the pool itself, nor is it emitted in an event on pool creation. The subsetHash for these pools will therefore need to be recreated by re-hashing the pool creation parameter calldata, or by introspecting the transaction itself. This missing data may in retrospect be a flaw in the design of the pools, however, for non-subset pools the hash is available on the factory contract itself and we can access that via a known method.
This change keeps all of the original functionality of the existing PositionManager, but increases usability by not requiring that the user pass a subsetHash parameter for isAjnaPool and mint functions.
Because the casual user does not have easy access to this variable, this modification would allow users to detect whether a pool is valid by simply passing the pool address to both isAjnaPool and mint, respectively.
Once a pool is added to the known list of Ajna pools using the permissionless addAjnaPool(pool, subsetHash) function, all future lookups to isAjnaPool are optimized to short-circuit and return without additional external calls to the factory contract, this results in a gas savings of approximately 20183 gas per each call to isAjnaPool, these savings are reflected in direct calls to the function and in each mint call, which are passed along to the user.
The contract can continue to be used as designed, and all existing tests are passing.
A community keeper or interested party can call addAjnaPool for approximately 30k gas one time, saving 20k gas on each future call to the PositionManager for that pool. This allows one community member to calculate the subsetHash for an ERC721 subset pool in the position manager and add it for general use without the subsetHash requirement.
Cons:
Function calls are increased by approximately 32 gas per call due to the additional mapping memory slot usage
Prototype does not modify PositionManager interface contract
ajnaPools mapping is internal, it may be useful for this to be public if this information is valuable elsewhere.
Additional explicit tests of the new and existing functions are needed.
Demonstration of gas savings
It is difficult to see the impact of this change at a per-function level when comparing against the test gas report, so I am including the standard test reports with gas consumption reports to give a better estimate of gas savings on a typical transaction flow. I've also added a PositionManager2.t.sol file with the exact same test suite, but with the inclusion of the addAjnaPool in the test setup to demonstrate the value of the optimized lookups. Please compare the results against the current design.
This is the existing test suite result after the introduction of these changes. This does increase gas costs generally in the tested workflow. However, this workflow does not generally account for multiple mints or lookups against a pool. This assumes that the pool has not been added to the PositionManager and factory lookups are still required.
This test suite is the same as PositionManager.t.sol above, with the addition of the pool to the PositionManager via addAjnaPool in the test setup. For all tests there is a reduction in cost of transaction flow across the board. Each pool only needs to be added to the PositionManager one time to achieve these savings. Once added, the flow cost of each test is less than in the current design. For popular pools, this could allow for a compounded gas savings for all users.
Description of change
High level
mint(pool)
andisAjnaPool(pool)
usage without requirement of subset hash.addAjnaPool
function. (Optional for non-subset pools)isAjnaPool
function .mint(pool, subsetHash)
calls, reducing subsequent mint costs against that pool.Description of bug or vulnerability and solution
Description of bug: Usability issue around subset hashes
subsetHash
is not available on the pool itself, nor is it emitted in an event on pool creation. The subsetHash for these pools will therefore need to be recreated by re-hashing the pool creation parameter calldata, or by introspecting the transaction itself. This missing data may in retrospect be a flaw in the design of the pools, however, for non-subset pools the hash is available on the factory contract itself and we can access that via a known method.subsetHash
parameter forisAjnaPool
andmint
functions.pool
address to bothisAjnaPool
andmint
, respectively.addAjnaPool(pool, subsetHash)
function, all future lookups toisAjnaPool
are optimized to short-circuit and return without additional external calls to the factory contract, this results in a gas savings of approximately20183
gas per each call toisAjnaPool
, these savings are reflected in direct calls to the function and in eachmint
call, which are passed along to the user.addAjnaPool
for approximately 30k gas one time, saving 20k gas on each future call to the PositionManager for that pool. This allows one community member to calculate the subsetHash for an ERC721 subset pool in the position manager and add it for general use without the subsetHash requirement.Cons:
ajnaPools
mapping isinternal
, it may be useful for this to bepublic
if this information is valuable elsewhere.Demonstration of gas savings
It is difficult to see the impact of this change at a per-function level when comparing against the test gas report, so I am including the standard test reports with gas consumption reports to give a better estimate of gas savings on a typical transaction flow. I've also added a
PositionManager2.t.sol
file with the exact same test suite, but with the inclusion of theaddAjnaPool
in the test setup to demonstrate the value of the optimized lookups. Please compare the results against the current design.Current PositionManager
master
After change
PositionManager.t.sol
This is the existing test suite result after the introduction of these changes. This does increase gas costs generally in the tested workflow. However, this workflow does not generally account for multiple mints or lookups against a pool. This assumes that the pool has not been added to the PositionManager and factory lookups are still required.
After change
PositionManager2.t.sol
This test suite is the same as
PositionManager.t.sol
above, with the addition of the pool to the PositionManager viaaddAjnaPool
in the test setup. For all tests there is a reduction in cost of transaction flow across the board. Each pool only needs to be added to the PositionManager one time to achieve these savings. Once added, the flow cost of each test is less than in the current design. For popular pools, this could allow for a compounded gas savings for all users.Contract size
Pre Change
Post Change
Gas usage
Pre Change
Post Change