Skip to content

Commit b0659d2

Browse files
authored
make fix, add getOwner (#111)
1 parent 5d70e15 commit b0659d2

File tree

7 files changed

+140
-23
lines changed

7 files changed

+140
-23
lines changed

auction-server/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

auction-server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "auction-server"
3-
version = "0.9.4"
3+
version = "0.9.5"
44
edition = "2021"
55
license-file = "license.txt"
66

contracts/ExpressRelay.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ struct MulticallData {
1010
address targetContract;
1111
bytes targetCalldata;
1212
uint256 bidAmount;
13+
uint256 gasLimit;
14+
bool revertOnFailure;
1315
}
1416
1517
function multicall(
@@ -28,8 +30,10 @@ When the relayer calls the `multicall` function it specifies a `permissionKey` a
2830
The contract will call the `targetContract` using the specified `targetCalldata` with `value:0`
2931
and expects the balance of the contract to be increased by `bidAmount` if the call was successful.
3032
Otherwise it will revert the the call and undo all the changes made by that call.
31-
The revert here is only for a specific call and not the whole transaction.
33+
Note the revert here is only for a specific call and not the whole transaction.
3234
A single transaction can contain multiple `MulticallData` where some of them fail and revert.
3335

36+
`gasLimit` specifies the maximum amount of gas that will be forwarded for the external call. `revertOnFailure` is a boolean that determines whether to revert the entire `ExpressRelay` transaction if the external call fails. This is only intended to be set to `true` for simulation purposes; for actual production on-chain submissions, this is intended to be set to `false` so that no individual external call's failure causes failure of the entire `Express Relay` transaction.
37+
3438
`isPermissioned` will return true for the specified `permissionKey`
3539
if and only if it is an inner call initiated by the `multicall` with the same `permissionKey`.

contracts/OpportunityAdapter.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ An opportunity refers to any arbitrary contract call with pre-defined expectatio
55
The opportunity adapter handles routing arbitrary calldata to an external contract along with assertions around the
66
quantity of ETH and different tokens exchanged as a result of the contract call.
77
The main function `executeOpportunity` accepts an `ExecutionParams` type.
8-
The `executor` should allow this contract to spend their tokens on their behalf using the ERC20 `approve` method.
98

109
```solidity
1110
struct TokenAmount {
@@ -14,7 +13,11 @@ struct TokenAmount {
1413
}
1514
1615
struct ExecutionParams {
17-
TokenAmount[] sellTokens;
16+
ISignatureTransfer.PermitBatchTransferFrom permit;
17+
ExecutionWitness witness;
18+
}
19+
20+
struct ExecutionWitness {
1821
TokenAmount[] buyTokens;
1922
address executor;
2023
address targetContract;
@@ -26,25 +29,27 @@ struct ExecutionParams {
2629
2730
```
2831

29-
by calling the `executeOpportunity` the contract will:
32+
The `ISignatureTransfer.PermitBatchTransferFrom` struct can be found [here](https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/interfaces/ISignatureTransfer.sol#L51-L58). This struct will contain the token(s) the user intends to sell and a nonce and deadline for the signature's validity.
33+
34+
by calling the `executeOpportunity` function the contract will:
3035

3136
1. Verify the parameters are valid:
3237
1. Verify it is being called from the `expressRelay` contract
33-
2. Verify that the executor actually signed this payload
34-
3. Verify this signature has not been used before
35-
4. Verify the block.timestamp is less than or equal to the `validUntil` parameter.
36-
5. Verifies there are no duplicate token addresses in `sellTokens` or `buyTokens`
37-
2. Transfers the sellTokens to the contract itself and approves the `targetContract` to use them
38-
3. Transfers `targetCallValue` Wrapped ETH from executor and converts them to ETH to be used as the value of the call
38+
2. Verify that the executor is the owner of the contract
39+
3. Verify the `targetContract` is not itself or the permit2 contract
40+
4. Verifies there are no duplicate token addresses in `permit.permitted` or `witness.buyTokens`
41+
2. Transfers the permitted "sell tokens" to itself via permit2 (which handles validation of the provided signature) and approves the `targetContract` to use them
42+
3. Converts the necessary amount of Wrapped ETH received from the executor to ETH to be used as the value of the call
3943
4. Calls the `targetContract` with `targetCalldata` and `targetCallValue`
40-
5. Checks that the contract has received the tokens specified in `buyTokens`
41-
6. Transfers the `buyTokens` back to the `executor`
44+
5. Revokes the allowances of the `targetContract` over the permitted sell tokens
45+
6. Checks that the contract has received the tokens specified in `buyTokens`
4246
7. Similar to 3, transfers `bidAmount` Wrapped ETH from executor and sends it to the express relay contract as the bid.
47+
8. Transfers the `buyTokens` back to the `executor`
4348

4449
If any of the mentioned steps fail the whole call will revert.
4550

46-
⚠️ Calling any ERC20 tokens with custom/malicious behaviour should be handled by the callee
47-
and is out of the scope for this contract.
51+
⚠️ Calling any ERC20 tokens with custom/malicious behaviour should be handled by the callee and is out of the scope for this contract.
52+
53+
⚠️ In cases where less than the specified amount of tokens is used by the target call, the remaining tokens will remain in the contract. Similarly, if less than the specified amount of (Wrapped) ETH is used by the target call, the remainder will remain in the contract as WETH. The owner of this contract can withdraw those assets via the `withdrawToken` function.
4854

49-
⚠️ In cases where less than the specified amount of tokens is used by the target call,
50-
the remaining tokens will remain in the contract and still approved to be used by the `targetContract`
55+
⚠️ If any ETH is transferred to the contract, the owner can withdraw the ETH via the `withdrawEth`

contracts/src/opportunity-adapter/OpportunityAdapter.sol

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ contract OpportunityAdapter is ReentrancyGuard, OpportunityAdapterHasher {
4343
_;
4444
}
4545

46+
/**
47+
* @notice getOwner function - returns the address of the owner of the contract
48+
*/
49+
function getOwner() public view returns (address) {
50+
return _owner;
51+
}
52+
4653
/**
4754
* @notice getExpressRelay function - returns the address of the express relay authenticated for calling this contract
4855
*/
@@ -250,12 +257,12 @@ contract OpportunityAdapter is ReentrancyGuard, OpportunityAdapterHasher {
250257
params.witness.targetCallValue
251258
);
252259
_revokeAllowances(params.permit, params.witness.targetContract);
260+
_settleBid(params.witness.bidAmount);
253261
_validateAndTransferBuyTokens(
254262
params.witness.buyTokens,
255263
params.witness.executor,
256264
buyTokensBalancesBeforeCall
257265
);
258-
_settleBid(params.witness.bidAmount);
259266
(
260267
uint256 ethBalanceAfterCall,
261268
uint256 wethBalanceAfterCall

contracts/test/OpportunityAdapterIntegration.t.sol

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,28 @@ import "permit2/interfaces/ISignatureTransfer.sol";
1515
import {PermitSignature, EIP712Domain} from "./PermitSignature.sol";
1616

1717
contract MockTarget {
18+
address payable _weth;
19+
20+
constructor(address weth) {
21+
_weth = payable(weth);
22+
}
23+
1824
error BadCall();
1925

2026
function doNothing() public payable {}
2127

28+
function exchangeWethForWeth(
29+
uint256 amountIn,
30+
uint256 amountOut
31+
) public payable {
32+
WETH9(_weth).transferFrom(msg.sender, address(this), amountIn);
33+
uint256 balanceWeth = WETH9(_weth).balanceOf(address(this));
34+
if (balanceWeth < amountOut) {
35+
WETH9(_weth).deposit{value: amountOut - balanceWeth}();
36+
}
37+
WETH9(_weth).transfer(msg.sender, amountOut);
38+
}
39+
2240
function transferTokenToSender(
2341
address token,
2442
uint256 amount
@@ -51,6 +69,7 @@ contract OpportunityAdapterIntegrationTest is
5169
WETH9 weth;
5270
MyToken buyToken;
5371
MyToken sellToken;
72+
address _expressRelay;
5473

5574
function setUpTokens() internal {
5675
buyToken = new MyToken("BuyToken", "BT");
@@ -59,8 +78,9 @@ contract OpportunityAdapterIntegrationTest is
5978
}
6079

6180
function setUpOpportunityAdapter() internal {
81+
_expressRelay = makeAddr("expressRelay");
6282
adapterFactory = new OpportunityAdapterFactory(
63-
address(this),
83+
_expressRelay,
6484
address(weth),
6585
PermitSignature.PERMIT2
6686
);
@@ -70,7 +90,7 @@ contract OpportunityAdapterIntegrationTest is
7090
setUpTokens();
7191
setUpPermit2();
7292
setUpOpportunityAdapter();
73-
mockTarget = new MockTarget();
93+
mockTarget = new MockTarget(address(weth));
7494
}
7595

7696
// successful bids will be received by this contract
@@ -252,7 +272,7 @@ contract OpportunityAdapterIntegrationTest is
252272
vm.prank(adapterFactory.getExpressRelay());
253273
vm.expectCall(address(mockTarget), callValue, targetCalldata);
254274
// We expect the adapter to transfer the bid to the express relay
255-
vm.expectCall(address(this), bid, bytes(""));
275+
vm.expectCall(_expressRelay, bid, bytes(""));
256276
vm.expectCall(
257277
address(weth),
258278
abi.encodeWithSelector(WETH9.withdraw.selector, callValue)
@@ -302,6 +322,87 @@ contract OpportunityAdapterIntegrationTest is
302322
adapterFactory.executeOpportunity(executionParams, signature);
303323
}
304324

325+
function testExecutionWithWethBuySellTokens(
326+
uint256 wethSellTokenAmount,
327+
uint256 wethBuyTokenAmount,
328+
uint256 bidAmount,
329+
uint256 targetCallValue
330+
) public {
331+
vm.assume(bidAmount < type(uint256).max - targetCallValue);
332+
vm.assume(
333+
wethSellTokenAmount <
334+
type(uint256).max - bidAmount - targetCallValue
335+
);
336+
vm.assume(
337+
wethBuyTokenAmount <
338+
type(uint256).max -
339+
wethSellTokenAmount -
340+
bidAmount -
341+
targetCallValue
342+
);
343+
344+
TokenAmount[] memory sellTokens = new TokenAmount[](1);
345+
uint256 sellTokenAmount = wethSellTokenAmount +
346+
bidAmount +
347+
targetCallValue;
348+
sellTokens[0] = TokenAmount(address(weth), sellTokenAmount);
349+
350+
TokenAmount[] memory buyTokens;
351+
if (wethBuyTokenAmount == 0) {
352+
buyTokens = new TokenAmount[](0);
353+
} else {
354+
buyTokens = new TokenAmount[](1);
355+
buyTokens[0] = TokenAmount(address(weth), wethBuyTokenAmount);
356+
}
357+
vm.deal(address(mockTarget), wethBuyTokenAmount);
358+
359+
bytes memory targetCalldata = abi.encodeWithSelector(
360+
mockTarget.exchangeWethForWeth.selector,
361+
wethSellTokenAmount,
362+
wethBuyTokenAmount
363+
);
364+
(
365+
ExecutionParams memory executionParams,
366+
bytes memory signature
367+
) = createExecutionParamsAndSignature(
368+
sellTokens,
369+
buyTokens,
370+
targetCalldata,
371+
targetCallValue,
372+
bidAmount,
373+
block.timestamp + 1000
374+
);
375+
376+
vm.deal(executionParams.witness.executor, sellTokenAmount);
377+
vm.startPrank(executionParams.witness.executor);
378+
weth.deposit{value: sellTokenAmount}();
379+
weth.approve(PERMIT2, sellTokenAmount);
380+
vm.stopPrank();
381+
382+
uint256 balanceWethPlusEthMockTargetPre = address(mockTarget).balance +
383+
WETH9(weth).balanceOf(address(mockTarget));
384+
385+
vm.prank(adapterFactory.getExpressRelay());
386+
vm.expectCall(address(mockTarget), targetCalldata);
387+
adapterFactory.executeOpportunity(executionParams, signature);
388+
389+
uint256 balanceWethPlusEthMockTargetPost = address(mockTarget).balance +
390+
WETH9(weth).balanceOf(address(mockTarget));
391+
392+
assertEq(
393+
WETH9(weth).balanceOf(executionParams.witness.executor),
394+
wethBuyTokenAmount
395+
);
396+
assertEq(adapterFactory.getExpressRelay().balance, bidAmount);
397+
assertEq(
398+
balanceWethPlusEthMockTargetPost,
399+
balanceWethPlusEthMockTargetPre -
400+
wethBuyTokenAmount +
401+
targetCallValue +
402+
wethSellTokenAmount
403+
);
404+
}
405+
305406
function testRevertWhenEthBalanceDecrease() public {
306407
TokenAmount[] memory noTokens = new TokenAmount[](0);
307408
bytes memory targetCalldata = abi.encodeWithSelector(

per_sdk/searcher/simple_searcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ async def main():
138138
parser.add_argument(
139139
"--bid",
140140
type=int,
141-
default=int(2e16), # To make sure it covers the gas cost
141+
default=int(5e17), # To make sure it covers the gas cost
142142
help="Default amount of bid for liquidation opportunities",
143143
)
144144
parser.add_argument(

0 commit comments

Comments
 (0)