Skip to content
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

Implement MerkleVCA for campaigns with variable claim amount #43

Open
smol-ninja opened this issue Jan 10, 2025 · 17 comments
Open

Implement MerkleVCA for campaigns with variable claim amount #43

smol-ninja opened this issue Jan 10, 2025 · 17 comments
Assignees
Labels
effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: feature New feature or request. work: complicated Sense-analyze-respond. The relationship between cause and effect requires analysis or expertise.

Comments

@smol-ninja
Copy link
Member

smol-ninja commented Jan 10, 2025

As discussed in https://github.com/sablier-labs/company-discussions/discussions/85, implement a new merkle contract with the following features:

  • The amount of airdropped token depends on the claim timestamp.
  • Only works for ranged streams i.e. all streams begin at the same time.
  • Enforce expiry to be greater than end time of the vesting schedule.
  • Nomenclatures TBD.
function createMerkleVCA(
    MerkleBase.ConstructorParams memory baseParams,
    uint40 startTime,
    uint40 endTime,
    uint256 aggregateAmount,
    uint256 recipientCount
)

Calculation of airdropped amount

$$\text{airdrop amount} = \text{claim amount} \cdot \frac{\text{block timestamp} - \text{start time}}{\text{total duration}}$$

Some questions

  1. Should it have start and cliff unlocks?
  2. What should the contract be called?
@smol-ninja smol-ninja added effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: feature New feature or request. work: complicated Sense-analyze-respond. The relationship between cause and effect requires analysis or expertise. labels Jan 10, 2025
@PaulRBerg
Copy link
Member

Thanks for creating the issue.

  1. Should it have start and cliff unlocks?

No. The first release should be as simple and slim as possible (to test the waters).

  1. What should the contract be called?

Not an easy choice. Some ideas:

  1. MerkleLinear
  2. MerkleVariable
  3. MerkleVCA
  4. MerkleFlex
  5. MerkleAutoCancel

I think the 1st would fit the best, but it might be confused with LL and/ or Lockup Linear streams.

@smol-ninja
Copy link
Member Author

I like MerkleVCA as its very clear once you know what VCA stands for.

  1. MerkleLinear: As you said, can cause confusion with LL.
  2. MerkleVariable: Sounds good, but "variable" can stand for many things.
  3. MerkleFlex: Not speaking for itself.
  4. MerkleAutoCancel: Technically, we are not cancelling any stream here. From the implementation's POV, the claim value is a function of time.

@PaulRBerg
Copy link
Member

Alright. MerkleVCA is the lucky winner.

@smol-ninja smol-ninja changed the title Implement a new Merkle campaign with variable claim amount Implement MerkleVCA with variable claim amount Jan 16, 2025
@smol-ninja smol-ninja changed the title Implement MerkleVCA with variable claim amount Implement MerkleVCA for campaigns with variable claim amount Jan 16, 2025
@smol-ninja smol-ninja self-assigned this Jan 24, 2025
@andreivladbrg
Copy link
Member

andreivladbrg commented Jan 29, 2025

function createMerkleVCA(
MerkleBase.ConstructorParams memory baseParams,
MerkleLL.Schedule memory schedule,
uint256 aggregateAmount,
uint256 recipientCount
)

why do we need a Schedule in AVCA? wasn't the plan to have only ranged streams?
thus we need only start time and end time.

@smol-ninja
Copy link
Member Author

You are right @andreivladbrg. I added it prior to the confirmation on cliff time and unlock amounts. I will remove it from the OP.

@andreivladbrg
Copy link
Member

got it

@smol-ninja
Copy link
Member Author

smol-ninja commented Feb 7, 2025

Pasting it here since its relevant to the design.

Previously, Cantina has recommended to make sure that the campaign deployment never fails to avoid loss of funds in case a campaign creator pre-funds the campaign before the deployment (create2). I have made the SablierMerkleVCA deployment fails if the following requirements are not met:

  1. If expiration is not zero, then it must be >= vesting.end + 30 days.
  2. vesting.end > vesting.start

My rationale is that unlike other Merkle campaigns where users are incentivised to claim early, in MerkleVCA, users are incentivised to claim late. That means, its entirely possible to see no claim until the vesting ends. So its important to check for the constraints in the constructor itself instead of the _claim function.

Please let me know if there is any suggestion or disagreement.

cc @razgraf for your comment as well.

@razgraf
Copy link
Member

razgraf commented Feb 7, 2025

Those 30 days, can we make them a variable that the user can pick a value for?

@smol-ninja
Copy link
Member Author

Of course. So, to confirm, if it's a variable, there could be campaigns where the user picks the expiration to be vesting.end + 1 second. Would that be OK? Or would adding a minimum duration be a better idea so that expiration >= vesting.end + minDurationHardCoded + user pick?

@razgraf
Copy link
Member

razgraf commented Feb 7, 2025

That's a good idea! I don't really see much "game value" in selecting something less than day 7 days, so a safe min. (minDurationHardCoded) would be helpful, on top of the user pick.

@smol-ninja
Copy link
Member Author

Makes sense. So, the following conditions in the constructor:

  1. If expiration is not zero, then it must be >= vesting.end + 7 days
  2. vesting.end > vesting.start

@PaulRBerg
Copy link
Member

Agree with the plan

@smol-ninja
Copy link
Member Author

Regarding the design, I have added a function called sacrificedAmount which returns the amount sacrificed by the early claimers. The value of sacrificedAmount is updated as the difference between original claim amount minus the actual claimed amount.

However, sender can only return it after the expiry. I first thought of creating a separate function to return sacrificedAmount at any time. But then I realized what if sender does not fully fund the campaign. He can refund the sacrificedAmount early causing loyal users not able to claim at the end of the vesting period.

Therefore, sacrificedAmount is there to get info on how much value had been sacrificed. In the end, after the expiry, sender can clawback what ever is left in the contract.

Let me know if there is any disagreement.

@PaulRBerg
Copy link
Member

Yes, those amounts should only be claw-backable after the expiration.

Can we say forfeitedAmount instead of sacrificedAmount?

'Forfeit' sounds less dramatic than 'sacrificed' 😅

@smol-ninja
Copy link
Member Author

smol-ninja commented Feb 11, 2025

Can we say forfeitedAmount instead of sacrificedAmount?

Sure. I intentionally omitted to use the word forfeit because it may mean a penalty imposed by the campaign creator on the early claimers. However, sacrifice signals a voluntarily act by the recipient.

@PaulRBerg
Copy link
Member

Good point. How about forgone?

@smol-ninja
Copy link
Member Author

Yeah. I feel nothing when I hear forgoneAmount so sounds less dramatic I guess

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: feature New feature or request. work: complicated Sense-analyze-respond. The relationship between cause and effect requires analysis or expertise.
Projects
None yet
Development

No branches or pull requests

4 participants