Skip to content

Commit a219130

Browse files
committed
added support for batch calls using OPERATION_TYPE_ERC2771_FORWARD_CALL
1 parent e1c0c7f commit a219130

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import {
99
} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
1010
import {SuperAppBase} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase.sol";
1111
import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
12+
import {IRelayRecipient} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/utils/IRelayRecipient.sol";
1213
import {IVestingSchedulerV3} from "./interface/IVestingSchedulerV3.sol";
1314
import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
1415
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
1516

16-
contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
17+
contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipient {
1718
using SuperTokenV1Library for ISuperToken;
1819

1920
ISuperfluid public immutable HOST;
@@ -86,7 +87,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
8687
_validateAndCreateVestingSchedule(
8788
ScheduleCreationParams({
8889
superToken: superToken,
89-
sender: msg.sender,
90+
sender: _msgSender(),
9091
receiver: receiver,
9192
startDate: _normalizeStartDate(startDate),
9293
claimValidityDate: claimValidityDate,
@@ -172,7 +173,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
172173
_validateAndCreateVestingSchedule(
173174
mapCreateVestingScheduleParams(
174175
superToken,
175-
msg.sender,
176+
_msgSender(),
176177
receiver,
177178
totalAmount,
178179
totalDuration,
@@ -518,7 +519,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
518519
VestingSchedule memory schedule = agg.schedule;
519520

520521
// Ensure that the caller is the sender or the receiver if the vesting schedule requires claiming.
521-
if (msg.sender != agg.sender && msg.sender != agg.receiver) {
522+
if (_msgSender() != agg.sender && _msgSender() != agg.receiver) {
522523
revert CannotClaimScheduleOnBehalf();
523524
}
524525

@@ -527,7 +528,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
527528
}
528529

529530
delete vestingSchedules[agg.id].claimValidityDate;
530-
emit VestingClaimed(agg.superToken, agg.sender, agg.receiver, msg.sender);
531+
emit VestingClaimed(agg.superToken, agg.sender, agg.receiver, _msgSender());
531532
}
532533

533534
/// @dev IVestingScheduler.executeCliffAndFlow implementation.
@@ -731,7 +732,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
731732
if (msg.sender != address(HOST)) revert HostInvalid();
732733
sender = HOST.decodeCtx(ctx).msgSender;
733734
} else {
734-
sender = msg.sender;
735+
sender = _msgSender();
735736
}
736737
// This is an invariant and should never happen.
737738
assert(sender != address(0));
@@ -745,4 +746,23 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase {
745746
function _getId(address superToken, address sender, address receiver) private pure returns (bytes32) {
746747
return keccak256(abi.encodePacked(superToken, sender, receiver));
747748
}
749+
750+
/// @dev IRelayRecipient.isTrustedForwarder implementation
751+
function isTrustedForwarder(address forwarder) public view override returns(bool) {
752+
return forwarder == HOST.getERC2771Forwarder();
753+
}
754+
755+
/// @dev IRelayRecipient.versionRecipient implementation
756+
function versionRecipient() external override pure returns (string memory) {
757+
return "v1";
758+
}
759+
760+
/// @dev gets the relayed sender from calldata as specified by EIP-2771, falling back to msg.sender
761+
function _msgSender() internal virtual view returns (address) {
762+
if(msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
763+
return address(bytes20(msg.data[msg.data.length - 20:]));
764+
} else {
765+
return msg.sender;
766+
}
767+
}
748768
}

packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,4 +2399,34 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester {
23992399
vestingScheduler.getVestingSchedule(address(superToken), alice, bob);
24002400
assertEq(schedule.endDate, newEndDate);
24012401
}
2402+
2403+
function test_use_2771_forward_call() public {
2404+
vm.startPrank(alice);
2405+
vestingScheduler.createVestingSchedule(
2406+
superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX
2407+
);
2408+
_arrangeAllowances(alice, FLOW_RATE);
2409+
vm.stopPrank();
2410+
2411+
vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE);
2412+
vestingScheduler.executeCliffAndFlow(superToken, alice, bob);
2413+
2414+
uint32 newEndDate = END_DATE + 1234;
2415+
ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1);
2416+
ops[0] = ISuperfluid.Operation({
2417+
operationType: BatchOperation.OPERATION_TYPE_ERC2771_FORWARD_CALL,
2418+
target: address(vestingScheduler),
2419+
data: abi.encodeCall(vestingScheduler.updateVestingScheduleEndDate, (superToken, bob, newEndDate, EMPTY_CTX))
2420+
});
2421+
2422+
// Act
2423+
vm.prank(alice);
2424+
sf.host.batchCall(ops);
2425+
vm.stopPrank();
2426+
2427+
// Assert
2428+
IVestingSchedulerV3.VestingSchedule memory schedule =
2429+
vestingScheduler.getVestingSchedule(address(superToken), alice, bob);
2430+
assertEq(schedule.endDate, newEndDate);
2431+
}
24022432
}

0 commit comments

Comments
 (0)