-
Notifications
You must be signed in to change notification settings - Fork 242
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ETHEREUM-CONTRACTS] App credit test (#1743)
* add app credit sanity test * cleanup console.logs
- Loading branch information
1 parent
bd9655d
commit 4f4d796
Showing
3 changed files
with
136 additions
and
4 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
packages/ethereum-contracts/contracts/mocks/CrossStreamSuperApp.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// SPDX-License-Identifier: AGPLv3 | ||
pragma solidity 0.8.19; | ||
|
||
import { ISuperfluid, ISuperToken } from "../interfaces/superfluid/ISuperfluid.sol"; | ||
import { SuperAppBaseFlow } from "../apps/SuperAppBaseFlow.sol"; | ||
import { SuperTokenV1Library } from "../apps/SuperTokenV1Library.sol"; | ||
|
||
using SuperTokenV1Library for ISuperToken; | ||
|
||
/// @title CrossStreamSuperApp | ||
/// @author Superfluid | ||
/// @dev A super app used for testing "cross-stream" flows in callbacks | ||
/// and its behavior surrounding the internal protocol accounting. | ||
/// That is, two senders sending a flow to the super app | ||
contract CrossStreamSuperApp is SuperAppBaseFlow { | ||
address public flowRecipient; | ||
address public prevSender; | ||
int96 public prevFlowRate; | ||
|
||
constructor(ISuperfluid host_, address z_) SuperAppBaseFlow(host_, true, true, true, "") { | ||
flowRecipient = z_; | ||
} | ||
|
||
function onFlowCreated(ISuperToken superToken, address sender, bytes calldata ctx) | ||
internal | ||
override | ||
returns (bytes memory newCtx) | ||
{ | ||
newCtx = ctx; | ||
|
||
// get incoming stream | ||
int96 inFlowRate = superToken.getFlowRate(sender, address(this)); | ||
|
||
if (prevSender == address(0)) { | ||
// first flow to super app creates a flow | ||
newCtx = superToken.createFlowWithCtx(flowRecipient, inFlowRate, newCtx); | ||
} else { | ||
// subsequent flows to super app updates and deletes the flow | ||
newCtx = superToken.updateFlowWithCtx(flowRecipient, inFlowRate, newCtx); | ||
newCtx = superToken.deleteFlowWithCtx(prevSender, address(this), newCtx); | ||
} | ||
|
||
prevSender = sender; | ||
prevFlowRate = inFlowRate; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
packages/ethereum-contracts/test/foundry/apps/CrossStreamSuperApp.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// SPDX-License-Identifier: AGPLv3 | ||
pragma solidity 0.8.19; | ||
|
||
import { CrossStreamSuperApp } from "../../../contracts/mocks/CrossStreamSuperApp.sol"; | ||
import { SuperTokenV1Library } from "../../../contracts/apps/SuperTokenV1Library.sol"; | ||
import { ISuperToken } from "../../../contracts/interfaces/superfluid/ISuperfluid.sol"; | ||
|
||
import { FoundrySuperfluidTester } from "../FoundrySuperfluidTester.sol"; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
using SuperTokenV1Library for ISuperToken; | ||
|
||
contract CrossStreamSuperAppTest is FoundrySuperfluidTester { | ||
CrossStreamSuperApp public superApp; | ||
|
||
constructor() FoundrySuperfluidTester(5) { } | ||
|
||
function setUp() public override { | ||
super.setUp(); | ||
|
||
superApp = new CrossStreamSuperApp(sf.host, bob); | ||
_addAccount(address(superApp)); | ||
} | ||
|
||
function testNoTokensMintedOrBurnedInCrossStreamSuperApp(int96 flowRate, uint64 blockTimestamp) public { | ||
vm.assume(flowRate < 1e14); | ||
// @note due to clipping, there is precision loss, therefore if the flow rate is too low | ||
// tokens will be unrecoverable | ||
vm.assume(flowRate > 2 ** 32 - 1); | ||
int96 initialFlowRate = flowRate; | ||
|
||
uint256 balance = superToken.balanceOf(alice); | ||
|
||
uint256 amountOfTimeTillZero = balance / uint256(uint96(initialFlowRate)); | ||
vm.assume(blockTimestamp < amountOfTimeTillZero); | ||
|
||
int256 rtbSumStart = _helperGetSuperTokenLiquiditySum(superToken); | ||
|
||
// send an initial deposit to the super app to allow the test to pass | ||
uint256 deposit = sf.cfa.getDepositRequiredForFlowRate(superToken, initialFlowRate); | ||
vm.startPrank(carol); | ||
superToken.transfer(address(superApp), deposit); | ||
vm.stopPrank(); | ||
|
||
// @note We are mainly concerned with _definitionAumGtEqRtbSumInvariant holding true: | ||
// Assert the global invariants at every step. | ||
_assertGlobalInvariants(); | ||
|
||
// create the first flow from alice -> super app -> bob | ||
vm.startPrank(alice); | ||
superToken.createFlow(address(superApp), initialFlowRate); | ||
vm.stopPrank(); | ||
|
||
_assertGlobalInvariants(); | ||
|
||
uint256 bt = block.timestamp; | ||
|
||
vm.warp(bt + blockTimestamp); | ||
|
||
assertEq(initialFlowRate, superToken.getFlowRate(alice, address(superApp))); | ||
|
||
// create the second flow from dan -> super app -> bob | ||
// this first: updates the flow from super app -> bob to equal to the new inflowRate (dan flow rateå) | ||
// then we delete the flow from alice -> super app, super app executes this deletion | ||
vm.startPrank(dan); | ||
int96 updatedFlowRate = initialFlowRate * 2; | ||
superToken.createFlow(address(superApp), updatedFlowRate); | ||
vm.stopPrank(); | ||
|
||
_assertGlobalInvariants(); | ||
|
||
// finally we close the stream from dan => super app and super app => bob | ||
vm.startPrank(dan); | ||
superToken.deleteFlow(dan, address(superApp)); | ||
vm.stopPrank(); | ||
|
||
_assertGlobalInvariants(); | ||
|
||
vm.startPrank(bob); | ||
superToken.deleteFlow(address(superApp), bob); | ||
vm.stopPrank(); | ||
|
||
_assertGlobalInvariants(); | ||
|
||
int256 rtbSumEnd = _helperGetSuperTokenLiquiditySum(superToken); | ||
|
||
assertEq(rtbSumStart, rtbSumEnd, "rtbSumStart != rtbSumEnd"); | ||
} | ||
} |