Skip to content
LogoLogo

Vesting

Ether sent to the contract vests linearly between start and start + duration. Anyone can call release, and the funds always go to the beneficiary.

vesting.lithe
(* Ether vests linearly to a beneficiary between `start` and `start + duration`.
   Anyone can trigger a release; the funds only ever go to the beneficiary. *)
 
import "std/address";
import "std/evm";
 
error AlreadyInitialized;
 
event Released of record {
  amount : wei;
}
 
storage is record {
  beneficiary : address;
  start : timestamp;
  duration : uint;
  released : wei;
}
 
external function init(const beneficiary : address; const start : timestamp; const duration : uint)
  raises AlreadyInitialized
is {
  assert(storage.beneficiary = Address.zero, AlreadyInitialized);
  storage.beneficiary := beneficiary;
  storage.start := start;
  storage.duration := duration;
}
 
external payable function fund() is {
  skip
}
 
view function vestedAt(const time : timestamp) : wei is {
  const total : wei := Evm.balance(Evm.self()) + storage.released;
  var vested : wei := 0wei;
  if time >= storage.start + storage.duration then {
    vested := total;
  } else if time > storage.start then {
    vested := wei(uint(total) * (time - storage.start) / storage.duration);
  }
} with vested
 
external view function releasable() : wei is
  vestedAt(Evm.blockTimestamp()) - storage.released
 
external nonreentrant function release() is {
  const amount : wei := vestedAt(Evm.blockTimestamp()) - storage.released;
  storage.released := storage.released + amount;
  emit Released { amount = amount; };
  defer Evm.transfer(storage.beneficiary, amount);
}