vault: change flows over time

This commit is contained in:
Mark Spanbroek 2025-01-28 14:58:14 +01:00
parent 82467f101d
commit 421a1eb5ba
3 changed files with 49 additions and 6 deletions

View File

@ -11,6 +11,7 @@ using {_tokensPerSecondPlus as +} for TokensPerSecond global;
using {_tokensPerSecondEquals as ==} for TokensPerSecond global; using {_tokensPerSecondEquals as ==} for TokensPerSecond global;
using {_tokensPerSecondNotEqual as !=} for TokensPerSecond global; using {_tokensPerSecondNotEqual as !=} for TokensPerSecond global;
using {_tokensPerSecondAtLeast as >=} for TokensPerSecond global; using {_tokensPerSecondAtLeast as >=} for TokensPerSecond global;
using {_tokensPerSecondLessThan as <} for TokensPerSecond global;
function _tokensPerSecondNegate( function _tokensPerSecondNegate(
TokensPerSecond rate TokensPerSecond rate
@ -54,3 +55,10 @@ function _tokensPerSecondAtLeast(
) pure returns (bool) { ) pure returns (bool) {
return TokensPerSecond.unwrap(a) >= TokensPerSecond.unwrap(b); return TokensPerSecond.unwrap(a) >= TokensPerSecond.unwrap(b);
} }
function _tokensPerSecondLessThan(
TokensPerSecond a,
TokensPerSecond b
) pure returns (bool) {
return TokensPerSecond.unwrap(a) < TokensPerSecond.unwrap(b);
}

View File

@ -179,22 +179,36 @@ abstract contract VaultBase {
Lock memory lock = _locks[controller][context]; Lock memory lock = _locks[controller][context];
require(lock.isLocked(), LockRequired()); require(lock.isLocked(), LockRequired());
Timestamp start = Timestamps.currentTime(); Balance memory senderBalance = _getBalance(controller, context, from);
Balance memory receiverBalance = _getBalance(controller, context, to);
Flow memory senderFlow = _flows[controller][context][from]; Flow memory senderFlow = _flows[controller][context][from];
Flow memory receiverFlow = _flows[controller][context][to];
Timestamp start = Timestamps.currentTime();
senderFlow.start = start; senderFlow.start = start;
senderFlow.rate = senderFlow.rate - rate; senderFlow.rate = senderFlow.rate - rate;
Flow memory receiverFlow = _flows[controller][context][to];
receiverFlow.start = start; receiverFlow.start = start;
receiverFlow.rate = receiverFlow.rate + rate; receiverFlow.rate = receiverFlow.rate + rate;
Balance memory senderBalance = _getBalance(controller, context, from); _checkFlowInvariant(senderBalance, lock, senderFlow);
uint128 flowMaximum = uint128(-senderFlow._totalAt(lock.maximum));
require(flowMaximum <= senderBalance.available, InsufficientBalance());
_balances[controller][context][from] = senderBalance;
_balances[controller][context][to] = receiverBalance;
_flows[controller][context][from] = senderFlow; _flows[controller][context][from] = senderFlow;
_flows[controller][context][to] = receiverFlow; _flows[controller][context][to] = receiverFlow;
} }
function _checkFlowInvariant(
Balance memory balance,
Lock memory lock,
Flow memory flow
) private pure {
if (flow.rate < TokensPerSecond.wrap(0)) {
uint128 outgoing = uint128(-flow._totalAt(lock.maximum));
require(outgoing <= balance.available, InsufficientBalance());
}
}
error InsufficientBalance(); error InsufficientBalance();
error Locked(); error Locked();
error AlreadyLocked(); error AlreadyLocked();

View File

@ -418,7 +418,6 @@ describe("Vault", function () {
return await vault.getBalance(context, recipient) return await vault.getBalance(context, recipient)
} }
it("requires that a lock is set", async function () { it("requires that a lock is set", async function () {
await expect(vault.flow(context, sender, receiver, 2)).to.be.revertedWith( await expect(vault.flow(context, sender, receiver, 2)).to.be.revertedWith(
"LockRequired" "LockRequired"
@ -471,6 +470,28 @@ describe("Vault", function () {
expect(await getBalance(receiver2)).to.equal(8) expect(await getBalance(receiver2)).to.equal(8)
}) })
it("can change flows over time", async function () {
await setAutomine(false)
await vault.flow(context, sender, receiver, 1)
await vault.flow(context, sender, receiver2, 2)
await mine()
const start = await currentTime()
advanceTimeTo(start + 4)
await vault.flow(context, receiver2, receiver, 1)
await mine()
expect(await getBalance(sender)).to.equal(deposit - 12)
expect(await getBalance(receiver)).to.equal(4)
expect(await getBalance(receiver2)).to.equal(8)
await advanceTimeTo(start + 8)
expect(await getBalance(sender)).to.equal(deposit - 24)
expect(await getBalance(receiver)).to.equal(12)
expect(await getBalance(receiver2)).to.equal(12)
await advanceTimeTo(start + 12)
expect(await getBalance(sender)).to.equal(deposit - 36)
expect(await getBalance(receiver)).to.equal(20)
expect(await getBalance(receiver2)).to.equal(16)
})
it("designates tokens that flow for the recipient", async function () { it("designates tokens that flow for the recipient", async function () {
await vault.flow(context, sender, receiver, 3) await vault.flow(context, sender, receiver, 3)
const start = await currentTime() const start = await currentTime()