Skip to content

Commit

Permalink
added batch call operations for upgradeTo and downgradeTo
Browse files Browse the repository at this point in the history
  • Loading branch information
d10r committed Jun 24, 2024
1 parent 4c86d26 commit c163b35
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,24 @@ library BatchOperation {
* )
*/
uint32 constant internal OPERATION_TYPE_SUPERTOKEN_DOWNGRADE = 2 + 100;
/**
* @dev SuperToken.upgradeTo batch operation type
*
* Call spec:
* ISuperToken(target).operationUpgradeTo(
* abi.decode(data, (address to, uint256 amount)
* )
*/
uint32 constant internal OPERATION_TYPE_SUPERTOKEN_UPGRADE_TO = 3 + 100;
/**
* @dev SuperToken.downgradeTo batch operation type
*
* Call spec:
* ISuperToken(target).operationDowngradeTo(
* abi.decode(data, (address to, uint256 amount)
* )
*/
uint32 constant internal OPERATION_TYPE_SUPERTOKEN_DOWNGRADE_TO = 4 + 100;
/**
* @dev Superfluid.callAgreement batch operation type
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,27 @@ interface ISuperToken is ISuperfluidToken, IERC20Metadata, IERC777 {
*/
function operationDowngrade(address account, uint256 amount) external;

/**
* @dev Upgrade ERC20 to SuperToken by host contract and transfer immediately.
* @param account The account to be changed.
* @param amount Number of tokens to be upgraded (in 18 decimals)
*
* @custom:modifiers
* - onlyHost
*/
function operationUpgradeTo(address account, address to, uint256 amount) external;

/**
* @dev Downgrade ERC20 to SuperToken by host contract and transfer immediately.
* @param account The account to be changed.
* @param to The account to receive upgraded tokens
* @param amount Number of tokens to be downgraded (in 18 decimals)
*
* @custom:modifiers
* - onlyHost
*/
function operationDowngradeTo(address account, address to, uint256 amount) external;

// Flow NFT events
/**
* @dev Constant Outflow NFT proxy created event
Expand Down
14 changes: 14 additions & 0 deletions packages/ethereum-contracts/contracts/superfluid/SuperToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,20 @@ contract SuperToken is
_downgrade(msg.sender, account, account, amount, "", "");
}

function operationUpgradeTo(address account, address to, uint256 amount)
external virtual override
onlyHost
{
_upgrade(msg.sender, account, to, amount, "", "");
}

function operationDowngradeTo(address account, address to, uint256 amount)
external virtual override
onlyHost
{
_downgrade(msg.sender, account, to, amount, "", "");
}

/**************************************************************************
* Modifiers
*************************************************************************/
Expand Down
14 changes: 13 additions & 1 deletion packages/ethereum-contracts/contracts/superfluid/Superfluid.sol
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ contract Superfluid is
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Superfluid Upgradeable Beacon
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// @inheritdoc ISuperfluid
function updatePoolBeaconLogic(address newLogic) external override onlyGovernance {
GeneralDistributionAgreementV1 gda = GeneralDistributionAgreementV1(
Expand Down Expand Up @@ -847,6 +847,18 @@ contract Superfluid is
ISuperToken(operations[i].target).operationDowngrade(
msgSender,
abi.decode(operations[i].data, (uint256))); // amount
} else if (operationType == BatchOperation.OPERATION_TYPE_SUPERTOKEN_UPGRADE_TO) {
(address to, uint256 amount) = abi.decode(operations[i].data, (address, uint256));
ISuperToken(operations[i].target).operationUpgradeTo(
msgSender,
to,
amount);
} else if (operationType == BatchOperation.OPERATION_TYPE_SUPERTOKEN_DOWNGRADE_TO) {
(address to, uint256 amount) = abi.decode(operations[i].data, (address, uint256));
ISuperToken(operations[i].target).operationDowngradeTo(
msgSender,
to,
amount);
} else if (operationType == BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_AGREEMENT) {
(bytes memory callData, bytes memory userData) = abi.decode(operations[i].data, (bytes, bytes));
_callAgreement(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,79 @@ contract SuperfluidBatchCallTest is FoundrySuperfluidTester {
vm.expectRevert("CallUtils: target revert()");
sf.host.batchCall{value: 42}(ops);
}

function testRevertIfOperationUpgradeToIsNotCalledByHost(address notHost) public {
vm.assume(notHost != address(sf.host));

vm.expectRevert(ISuperfluidToken.SF_TOKEN_ONLY_HOST.selector);
vm.prank(notHost);
superToken.operationUpgradeTo(alice, bob, 100);
}

function testUpgradeTo(uint256 amount) public {
vm.assume(amount < type(uint64).max);

vm.prank(alice);
token.approve(address(superToken), amount);

uint256 bobBalanceBefore = superToken.balanceOf(bob);
vm.prank(address(sf.host));
superToken.operationUpgradeTo(alice, bob, amount);
uint256 bobBalanceAfter = superToken.balanceOf(bob);
assertEq(bobBalanceAfter, bobBalanceBefore + amount, "Bob has unexpected final balance");
}

function testUpgradeToBatchCall(uint256 amount) public {
vm.assume(amount < type(uint64).max);

vm.prank(alice);
token.approve(address(superToken), amount);

ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1);
uint256 bobBalanceBefore = superToken.balanceOf(bob);
ops[0] = ISuperfluid.Operation({
operationType: BatchOperation.OPERATION_TYPE_SUPERTOKEN_UPGRADE_TO,
target: address(superToken),
data: abi.encode(bob, amount)
});
vm.prank(alice);
sf.host.batchCall(ops);
uint256 bobBalanceAfter = superToken.balanceOf(bob);
assertEq(bobBalanceAfter, bobBalanceBefore + amount, "Bob has unexpected final balance");
}

function testRevertIfOperationDowngradeToIsNotCalledByHost(address notHost) public {
vm.assume(notHost != address(sf.host));

vm.expectRevert(ISuperfluidToken.SF_TOKEN_ONLY_HOST.selector);
vm.prank(notHost);
superToken.operationDowngradeTo(alice, bob, 100);
}

function testDowngradeTo(uint256 amount) public {
vm.assume(amount < type(uint64).max);

uint256 bobBalanceBefore = token.balanceOf(bob);
vm.prank(address(sf.host));
superToken.operationDowngradeTo(alice, bob, amount);
uint256 bobBalanceAfter = token.balanceOf(bob);
assertEq(bobBalanceAfter, bobBalanceBefore + amount, "Bob has unexpected final balance");
}

function testDowngradeToBatchCall(uint256 amount) public {
vm.assume(amount < type(uint64).max);

ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1);
uint256 bobBalanceBefore = token.balanceOf(bob);
ops[0] = ISuperfluid.Operation({
operationType: BatchOperation.OPERATION_TYPE_SUPERTOKEN_DOWNGRADE_TO,
target: address(superToken),
data: abi.encode(bob, amount)
});
vm.prank(alice);
sf.host.batchCall(ops);
uint256 bobBalanceAfter = token.balanceOf(bob);
assertEq(bobBalanceAfter, bobBalanceBefore + amount, "Bob has unexpected final balance");
}

}

0 comments on commit c163b35

Please sign in to comment.