-
Notifications
You must be signed in to change notification settings - Fork 359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add erc4626 #1170
base: main
Are you sure you want to change the base?
Add erc4626 #1170
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #1170 +/- ##
==========================================
- Coverage 92.26% 89.41% -2.86%
==========================================
Files 59 81 +22
Lines 1811 3496 +1685
==========================================
+ Hits 1671 3126 +1455
- Misses 140 370 +230
... and 78 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @andrew-fleming! Left some non-blocking suggestions.
asset_address: ContractAddress, shares: u256, recipient: ContractAddress, | ||
) -> ERC4626ABIDispatcher { | ||
let fee_basis_points = 500_u256; // 5% | ||
let _value_without_fees = 10_000_u256; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the leading underscore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No good reason. Fixed!
let fee_basis_points = 500_u256; // 5% | ||
let _value_without_fees = 10_000_u256; | ||
let _fees = (_value_without_fees * fee_basis_points) / 10_000_u256; | ||
let _value_with_fees = _value_without_fees - _fees; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this value used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, fixed!
Co-authored-by: Eric Nordelo <[email protected]>
@ericnordelo when we have |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job, left a few minor suggestions
fn max_withdraw(self: @ComponentState<TContractState>, owner: ContractAddress) -> u256 { | ||
match Limit::withdraw_limit(self, owner) { | ||
Option::Some(limit) => limit, | ||
Option::None => { | ||
let erc20_component = get_dep_component!(self, ERC20); | ||
let owner_shares = erc20_component.balance_of(owner); | ||
self._convert_to_assets(owner_shares, Rounding::Floor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think, what amount should we return from max_withdraw
call if there IS a limit of, let's say, 1000
, and the owner's balance is 300? Without a limit the function will return 300
, but if there is one, the value of 1000
will be returned.
Maybe we should return the balance if it's less than the limit? Same applies to redeem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed! Good catch
Co-authored-by: immrsd <[email protected]>
WIP
Fixes #???
PR Checklist
...still a WIP. The tests need to be refactored and improved. The idea was to make sure the logic works as expected so there's a standard (from the openzeppelin solidity tests) for any and all changes moving forward.
Some things to note and discuss:
Decimals
EIP4626 states at the end of the doc:
The OpenZeppelin solidity implementation checks for the underlying asset's tokens upon construction with a try/catch which defaults to
18
decimals if query fails. Given Starknet's current status with try/catch (✌️ dual dispatchers), this doesn't seem like a viable approach. If the vault is for an undeployed token, the deployment will fail. This PR proposes that the decimals (both underlying decimals and offset decimals) are explicitly defined by the contract through the ImmutableConfig.Math
u512 Precision math for multiply and divide (
u256_mul_div
)This PR leverages the corelib's
wide_mul
andu512_safe_div_rem_by_u256
for mathematical precision. This is set as a tmp solution and should be scrutinized further. More tests should be provided and we should look for ways to optimize.Exponentiation
This PR requires exponentiation for converting to shares and assets. The current implementation is the brute force formula. We can definitely improve this.This was added to the corelib (starkware-libs/cairo#6694). Will update when released
FeeConfigTrait
This PR proposes to utilize
FeeConfigTrait
(which is really like a hook) for contracts to integrate entry and exit fees forpreview_
fns. The state-changing methods of ERC4626 rely onpreview_
to determine the number of assets or shares to exchange.Another approach that can reduce the verbosity of the traits/hooks is to have a single
adjust_assets_or_shares
function that accepts anExchangeType
.The downside though is I think it's easy to misuse i.e.
IMO having a dedicated function for each exchange type is more difficult to mess up...but it's at the cost of some verbosity.
LimitsConfigTrait
This mirrors the
FeeConfigTrait
except that these target the limits on themax_
methods and return anOption
soOption::None
can point to the default. Same arguments apply for not having a single trait/hook with anExchangeType
parameter.before_withdraw
andafter_deposit
hooksThe
before_withdraw
andafter_deposit
hooks take inspiration from solmate's solidity implementation of erc4626. These hooks are where contracts can transfer fees calculated from theFeeConfigTrait
in the implementing contract. See the Fees mock to see how this works in the proposed PR.