Conversation
rob1997
left a comment
There was a problem hiding this comment.
Good work! Clean PR I like it but needs some changes
| } | ||
|
|
||
| function getSubscriptionDuration(uint256 value) external view returns (uint256) { | ||
| return (value / subscriptionPrice) * SUBSCRIPTION_DURATION; |
There was a problem hiding this comment.
This method should enforce value to be a multiple of subscriptionPrice in which case getSubscriptionPriceAndDuration already covers its use case, so it's not relevant.
| @@ -146,7 +177,7 @@ contract Asset is Ownable, ReentrancyGuard, IAsset { | |||
| } | |||
|
|
|||
| function _subscribe(bytes32 subscriber, address payer, uint256 value) internal returns (uint256) { | |||
There was a problem hiding this comment.
based on this new implementation, value should be refactored to count instead, so value = count * subscriptionPrice and duration = subscriptionDuration * count, subscribe should be changed from
subscribe(bytes32 subscriber, address payer, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
to
subscribe(bytes32 subscriber, address payer, address spender, uint256 count, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
signature is the same but value and duration derivation changes
| bool isRegistry, | ||
| uint256 timestamp | ||
| ) internal view returns (uint256 claimable, uint256 claimedNonce) { | ||
| ) internal view returns (uint256 claimable, uint256 claimedNonce, uint256 newClaimedAtTimestamp) { |
There was a problem hiding this comment.
The claim function should not change, the Asset/Registry Owner should still be able to claim all the amount due. If this is a fix for a flaw/bug lmk but if this is just an optimization please create another PR for it
There was a problem hiding this comment.
It was added to avoid losing dust during claiming. If we allow claiming for partial periods then some money will be lost in this computation and stuck in the contract (for example, the price for the 2-week period is 5, we claim for 1 week, half of that, round to 2, then claim for the other half, round to 2 again, and 1 is lost). One of the solutions is to claim only for whole passed periods, that's why the time is clamped to the end of the period and timestamps are stored. Let me know if we should choose another solution
There was a problem hiding this comment.
Right yeah, Good catch!
One of the solutions is to claim only for whole passed periods
Yeah this is a good rule, lets go with this!
| // Active subscription: mark cancelled immediately; truncate endTime to end of last | ||
| // started period so _claimable charges the full in-progress period as fee | ||
| subscriptions[id].cancelled = true; | ||
| subscriptions[id].endTime = subscription.endTime - (refundablePeriods * SUBSCRIPTION_DURATION); |
There was a problem hiding this comment.
while emitting SubscriptionCancelled perhaps we should also add the new endTime of the subscription as an input
|
|
||
| uint256 returnable = 0; | ||
| // Effective start for refund: use startTime for future subscriptions, timestamp for active subscriptions | ||
| uint256 effectiveStart = subscription.startTime >= timestamp ? subscription.startTime : timestamp; |
There was a problem hiding this comment.
if (subscription.startTime >= timestamp) subscription hasn't started yet, considering that subscription duration must be a multiple of subscriptionDuration refundable amount would be equal to ((endTime - startTime) / subscriptionDuration) * subscriptionPrice, we can avoid some extra computation.
| // and payer are the same (and it was not cancelled). | ||
| if ( | ||
| startTime == subscription.endTime && subscription.payer == payer | ||
| !subscription.cancelled && startTime == subscription.endTime && subscription.payer == payer |
There was a problem hiding this comment.
We should allow subscription extensions even after a subscription has been cancelled
There was a problem hiding this comment.
Since endTime - startTime is still a multiple of subscriptionDuration even after cancellation it shouldn't be an issue
There was a problem hiding this comment.
In the current implementation endTime can be in the future for the cancelled subscription. What should the behavior be? I suppose we should create a new subscription, not extend the cancelled one
There was a problem hiding this comment.
We should still be able to extend it, there's no need to add a new index/nonce
| uint256 subscriptionPrice; | ||
| uint256 registryFeeShare; | ||
| address payer; | ||
| bool cancelled; |
There was a problem hiding this comment.
Is this additional field cancelled to avoid extra computation if a subscriber tries to cancel again? Which isn't consequential because it's unlikely and the computation is also negligible since we start the loop from the latest subscription and the condition check should be simple enough
if ((endTime - timestamp) / subscriptionDuration == 0) break;
There was a problem hiding this comment.
The cancelled field was added to allow claiming fees for the future part of the current period. If we just set endTime for the subscription to the current timestamp then the claiming should be modified as well because it would lose the future part of the fee. In the current version, the endTime is clamped to the end of the current period to allow full fee collection, but it would mean that the subscription stays active without introducing the cancelled flag. We can discuss other options
There was a problem hiding this comment.
I'm not suggesting we set endTime to current timestamp, it should be set to the end of the current period yes but claiming in its current implementation can only claim any amount till the current timestamp. Consider the following
duration = 3
startTime = 2
endTime = 14
This means subscribed for 4 periods, if subscriber cancels at timestamp = 6 so now endTime can now be 8. 8 - 14 (2 periods) is refundable. Asset/Registry owners can still claim for duration 2 - 8 until timestamp == 8
There was a problem hiding this comment.
yes, that's how claiming will work now: only for fully passed periods. If we set endTime to the end of the current period then the subscription will be still considered active without the cancelled flag
Implements #31