Skip to content

Commit 3a44ade

Browse files
feat: GN-1628 immutable GelatoRelayERC2771 and callWithSyncFeeERC2771 (#46)
1 parent c5152df commit 3a44ade

38 files changed

+2167
-451
lines changed

contracts/GelatoRelay.sol

Lines changed: 13 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import {IGelato1Balance} from "./interfaces/IGelato1Balance.sol";
66
import {GelatoRelayBase} from "./abstract/GelatoRelayBase.sol";
77
import {GelatoCallUtils} from "./lib/GelatoCallUtils.sol";
88
import {GelatoTokenUtils} from "./lib/GelatoTokenUtils.sol";
9+
import {SponsoredCall} from "./types/CallTypes.sol";
910
import {
10-
_encodeGelatoRelayContext,
11-
_encodeFeeCollector
11+
_encodeFeeCollector,
12+
_encodeRelayContext
1213
} from "@gelatonetwork/relay-context/contracts/functions/GelatoRelayUtils.sol";
1314
import {
1415
__getFeeCollector
@@ -18,9 +19,8 @@ import {
1819
_getFeeTokenRelayContext,
1920
_getFeeRelayContext
2021
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
21-
import {_eip2771Context} from "./functions/ContextUtils.sol";
22-
import {SponsoredCall, SponsoredUserAuthCall} from "./types/CallTypes.sol";
23-
import {IGelato} from "./interfaces/IGelato.sol";
22+
// backwards compatible encoding for msg.sender support instead of feeCollector
23+
import {_deprecatedRelayContext} from "./functions/DeprecatedUtils.sol";
2424

2525
/// @title Gelato Relay contract
2626
/// @notice This contract deals with synchronous payments and Gelato 1Balance payments
@@ -34,7 +34,7 @@ contract GelatoRelay is IGelatoRelay, IGelato1Balance, GelatoRelayBase {
3434
//solhint-disable-next-line const-name-snakecase
3535
string public constant name = "GelatoRelay";
3636
//solhint-disable-next-line const-name-snakecase
37-
string public constant version = "1";
37+
string public constant version = "2";
3838

3939
// solhint-disable-next-line no-empty-blocks
4040
constructor(address _gelato) GelatoRelayBase(_gelato) {}
@@ -50,7 +50,7 @@ contract GelatoRelay is IGelatoRelay, IGelato1Balance, GelatoRelayBase {
5050
uint256 preBalance = _feeToken.getBalance(address(this));
5151

5252
_target.revertingContractCall(
53-
_encodeGelatoRelayContext(_data, msg.sender, _feeToken, _fee),
53+
_deprecatedRelayContext(_data, msg.sender, _feeToken, _fee),
5454
"GelatoRelay.callWithSyncFee:"
5555
);
5656

@@ -68,25 +68,25 @@ contract GelatoRelay is IGelatoRelay, IGelato1Balance, GelatoRelayBase {
6868
/// @dev This is the most straightforward use case, and `transfer` handles token payments.
6969
/// @param _target Target smart contract
7070
/// @param _data Payload for call on _target
71-
/// @param _relayContext true: all relay context encoding, false: only feeCollector encoding
71+
/// @param _isRelayContext true: all relay context encoding, false: only feeCollector encoding
7272
/// @param _correlationId Unique task identifier generated by gelato
7373
function callWithSyncFeeV2(
7474
address _target,
7575
bytes calldata _data,
76-
bool _relayContext,
76+
bool _isRelayContext,
7777
bytes32 _correlationId
7878
) external onlyGelato {
7979
address feeToken;
8080
uint256 preBalance;
8181

82-
if (_relayContext) {
82+
if (_isRelayContext) {
8383
feeToken = _getFeeTokenRelayContext();
8484
preBalance = feeToken.getBalance(address(this));
8585
}
8686

87-
_relayContext
87+
_isRelayContext
8888
? _target.revertingContractCall(
89-
_encodeGelatoRelayContext(
89+
_encodeRelayContext(
9090
_data,
9191
_getFeeCollectorRelayContext(),
9292
feeToken,
@@ -99,7 +99,7 @@ contract GelatoRelay is IGelatoRelay, IGelato1Balance, GelatoRelayBase {
9999
"GelatoRelay.callWithSyncFeeV2:"
100100
);
101101

102-
if (_relayContext) {
102+
if (_isRelayContext) {
103103
uint256 fee = feeToken.getBalance(address(this)) - preBalance;
104104
if (fee != 0) feeToken.transfer(msg.sender, fee);
105105
}
@@ -145,71 +145,6 @@ contract GelatoRelay is IGelatoRelay, IGelato1Balance, GelatoRelayBase {
145145
);
146146
}
147147

148-
/// @notice Relay call + One Balance payment - with BOTH sponsor and user authentication
149-
/// @notice Both sponsor and user signature allows for payment via sponsor's 1Balance balance
150-
/// @dev Payment is handled with off-chain accounting using Gelato's 1Balance system
151-
/// @dev The userNonce abstraction does not support multiple calls (call concurrency)
152-
/// @dev Apps that need concurrent user calls will need to implement multi-calling
153-
/// @dev on their end via encoding into _call.data.
154-
/// @param _call Relay call data packed into SponsoredUserAuthCall struct
155-
/// @param _userSignature EIP-712 compliant signature from _call.user
156-
/// @param _nativeToFeeTokenXRateNumerator Exchange rate numerator
157-
/// @param _nativeToFeeTokenXRateDenominator Exchange rate denominator
158-
/// @param _correlationId Unique task identifier generated by gelato
159-
// solhint-disable-next-line function-max-lines
160-
function sponsoredUserAuthCall(
161-
SponsoredUserAuthCall calldata _call,
162-
address _sponsor,
163-
address _feeToken,
164-
uint256 _oneBalanceChainId,
165-
bytes calldata _userSignature,
166-
uint256 _nativeToFeeTokenXRateNumerator,
167-
uint256 _nativeToFeeTokenXRateDenominator,
168-
bytes32 _correlationId
169-
) external onlyGelato {
170-
// CHECKS
171-
_requireChainId(_call.chainId, "GelatoRelay.sponsoredUserAuthCall:");
172-
173-
uint256 storedUserNonce = userNonce[_call.user];
174-
175-
// For the user, we enforce nonce ordering
176-
_requireUserBasics(
177-
_call.userNonce,
178-
storedUserNonce,
179-
_call.userDeadline,
180-
"GelatoRelay.sponsoredUserAuthCall:"
181-
);
182-
183-
bytes32 domainSeparator = _getDomainSeparator();
184-
185-
// Verify user's signature
186-
_requireSponsoredUserAuthCallSignature(
187-
domainSeparator,
188-
_call,
189-
_userSignature,
190-
_call.user
191-
);
192-
193-
// EFFECTS
194-
userNonce[_call.user] = storedUserNonce + 1;
195-
196-
// INTERACTIONS
197-
_call.target.revertingContractCall(
198-
_eip2771Context(_call.data, _call.user),
199-
"GelatoRelay.sponsoredUserAuthCall:"
200-
);
201-
202-
emit LogUseGelato1Balance(
203-
_sponsor,
204-
_call.target,
205-
_feeToken,
206-
_oneBalanceChainId,
207-
_nativeToFeeTokenXRateNumerator,
208-
_nativeToFeeTokenXRateDenominator,
209-
_correlationId
210-
);
211-
}
212-
213148
//solhint-disable-next-line func-name-mixedcase
214149
function DOMAIN_SEPARATOR() external view returns (bytes32) {
215150
return _getDomainSeparator();

contracts/GelatoRelayERC2771.sol

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.17;
3+
4+
import {IGelatoRelayERC2771} from "./interfaces/IGelatoRelayERC2771.sol";
5+
import {IGelato1Balance} from "./interfaces/IGelato1Balance.sol";
6+
import {GelatoRelayERC2771Base} from "./abstract/GelatoRelayERC2771Base.sol";
7+
import {GelatoCallUtils} from "./lib/GelatoCallUtils.sol";
8+
import {GelatoTokenUtils} from "./lib/GelatoTokenUtils.sol";
9+
import {CallWithERC2771} from "./types/CallTypes.sol";
10+
import {
11+
_encodeERC2771Context,
12+
_encodeFeeCollectorERC2771,
13+
_encodeRelayContextERC2771
14+
} from "@gelatonetwork/relay-context/contracts/functions/GelatoRelayUtils.sol";
15+
import {
16+
_getFeeCollectorRelayContext,
17+
_getFeeRelayContext
18+
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
19+
import {
20+
__getFeeCollector
21+
} from "@gelatonetwork/relay-context/contracts/GelatoRelayFeeCollector.sol";
22+
23+
/// @title Gelato Relay contract
24+
/// @notice This contract deals with synchronous payments and Gelato 1Balance payments
25+
/// @dev This contract must NEVER hold funds!
26+
/// @dev Maliciously crafted transaction payloads could wipe out any funds left here
27+
// solhint-disable-next-line max-states-count
28+
contract GelatoRelayERC2771 is
29+
IGelatoRelayERC2771,
30+
IGelato1Balance,
31+
GelatoRelayERC2771Base
32+
{
33+
using GelatoCallUtils for address;
34+
using GelatoTokenUtils for address;
35+
36+
//solhint-disable-next-line const-name-snakecase
37+
string public constant name = "GelatoRelayERC2771";
38+
//solhint-disable-next-line const-name-snakecase
39+
string public constant version = "1";
40+
41+
// solhint-disable-next-line no-empty-blocks
42+
constructor(address _gelato) GelatoRelayERC2771Base(_gelato) {}
43+
44+
/// @notice Relay call with Synchronous Payment
45+
/// @notice The target contract pays Gelato during the call forward
46+
/// @dev This is the most straightforward use case, and `transfer` handles token payments.
47+
/// @param _call Relay call data packed into CallWithERC2771 struct
48+
/// @param _isRelayContext true: all relay context encoding, false: only feeCollector encoding
49+
/// @param _correlationId Unique task identifier generated by gelato
50+
// solhint-disable-next-line function-max-lines
51+
function callWithSyncFeeERC2771(
52+
CallWithERC2771 calldata _call,
53+
address _feeToken,
54+
bytes calldata _userSignature,
55+
bool _isRelayContext,
56+
bytes32 _correlationId
57+
) external onlyGelato {
58+
// CHECKS
59+
_requireChainId(
60+
_call.chainId,
61+
"GelatoRelayERC2771.callWithSyncFeeERC2771:"
62+
);
63+
64+
uint256 storedUserNonce = userNonce[_call.user];
65+
66+
// For the user, we enforce nonce ordering
67+
_requireUserBasics(
68+
_call.userNonce,
69+
storedUserNonce,
70+
_call.userDeadline,
71+
"GelatoRelayERC2771.callWithSyncFeeERC2771:"
72+
);
73+
74+
bytes32 domainSeparator = _getDomainSeparator();
75+
76+
// Verify user's signature
77+
_requireCallWithSyncFeeERC2771Signature(
78+
domainSeparator,
79+
_call,
80+
_userSignature,
81+
_call.user
82+
);
83+
84+
// EFFECTS
85+
userNonce[_call.user] = storedUserNonce + 1;
86+
87+
// INTERACTIONS
88+
_isRelayContext
89+
? _call.target.revertingContractCall(
90+
_encodeRelayContextERC2771(
91+
_call.data,
92+
_getFeeCollectorRelayContext(),
93+
_feeToken,
94+
_getFeeRelayContext(),
95+
_call.user
96+
),
97+
"GelatoRelayERC2771.callWithSyncFeeERC2771:"
98+
)
99+
: _call.target.revertingContractCall(
100+
_encodeFeeCollectorERC2771(
101+
_call.data,
102+
__getFeeCollector(),
103+
_call.user
104+
),
105+
"GelatoRelayERC2771.callWithSyncFeeERC2771:"
106+
);
107+
108+
emit LogCallWithSyncFeeERC2771(_call.target, _correlationId);
109+
}
110+
111+
/// @notice Relay call + One Balance payment with _msgSender user signature verification
112+
/// @dev Payment is handled with off-chain accounting using Gelato's 1Balance system
113+
/// @dev The userNonce abstraction does not support multiple calls (call concurrency)
114+
/// @dev Apps that need concurrent user calls will need to implement multi-calling
115+
/// @dev on their end via encoding into _call.data.
116+
/// @param _call Relay call data packed into CallWithERC2771 struct
117+
/// @param _userSignature EIP-712 compliant signature from _call.user
118+
/// @param _nativeToFeeTokenXRateNumerator Exchange rate numerator
119+
/// @param _nativeToFeeTokenXRateDenominator Exchange rate denominator
120+
/// @param _correlationId Unique task identifier generated by gelato
121+
// solhint-disable-next-line function-max-lines
122+
function sponsoredCallERC2771(
123+
CallWithERC2771 calldata _call,
124+
address _sponsor,
125+
address _feeToken,
126+
uint256 _oneBalanceChainId,
127+
bytes calldata _userSignature,
128+
uint256 _nativeToFeeTokenXRateNumerator,
129+
uint256 _nativeToFeeTokenXRateDenominator,
130+
bytes32 _correlationId
131+
) external onlyGelato {
132+
// CHECKS
133+
_requireChainId(
134+
_call.chainId,
135+
"GelatoRelayERC2771.sponsoredCallERC2771:"
136+
);
137+
138+
uint256 storedUserNonce = userNonce[_call.user];
139+
140+
// For the user, we enforce nonce ordering
141+
_requireUserBasics(
142+
_call.userNonce,
143+
storedUserNonce,
144+
_call.userDeadline,
145+
"GelatoRelayERC2771.sponsoredCallERC2771:"
146+
);
147+
148+
bytes32 domainSeparator = _getDomainSeparator();
149+
150+
// Verify user's signature
151+
_requireSponsoredCallERC2771Signature(
152+
domainSeparator,
153+
_call,
154+
_userSignature,
155+
_call.user
156+
);
157+
158+
// EFFECTS
159+
userNonce[_call.user] = storedUserNonce + 1;
160+
161+
// INTERACTIONS
162+
_call.target.revertingContractCall(
163+
_encodeERC2771Context(_call.data, _call.user),
164+
"GelatoRelayERC2771.sponsoredCallERC2771:"
165+
);
166+
167+
emit LogUseGelato1Balance(
168+
_sponsor,
169+
_call.target,
170+
_feeToken,
171+
_oneBalanceChainId,
172+
_nativeToFeeTokenXRateNumerator,
173+
_nativeToFeeTokenXRateDenominator,
174+
_correlationId
175+
);
176+
}
177+
178+
//solhint-disable-next-line func-name-mixedcase
179+
function DOMAIN_SEPARATOR() external view returns (bytes32) {
180+
return _getDomainSeparator();
181+
}
182+
183+
function _getDomainSeparator() internal view returns (bytes32) {
184+
return
185+
keccak256(
186+
abi.encode(
187+
keccak256(
188+
bytes(
189+
//solhint-disable-next-line max-line-length
190+
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
191+
)
192+
),
193+
keccak256(bytes(name)),
194+
keccak256(bytes(version)),
195+
block.chainid,
196+
address(this)
197+
)
198+
);
199+
}
200+
}

contracts/__mocks__/MockGelatoRelayContext.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ contract MockGelatoRelayContext is GelatoRelayContext {
1010
event LogContext(address feeCollector, address feeToken, uint256 fee);
1111

1212
function emitContext() external {
13-
emit LogMsgData(__msgData());
13+
emit LogMsgData(_getMsgData());
1414
emit LogContext(_getFeeCollector(), _getFeeToken(), _getFee());
1515
}
1616

0 commit comments

Comments
 (0)