Skip to content

Commit

Permalink
Merge branch 'main' into 3070/add-is-winer-to-solver-competition-endp…
Browse files Browse the repository at this point in the history
…oint
  • Loading branch information
m-lord-renkse authored Dec 9, 2024
2 parents 4791d80 + 78a1b81 commit 9df6582
Show file tree
Hide file tree
Showing 19 changed files with 868 additions and 82 deletions.
2 changes: 1 addition & 1 deletion crates/contracts/artifacts/Solver.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/contracts/artifacts/Spardose.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"abi":[{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"requestFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}],"bytecode":"0x608060405234801561001057600080fd5b506102de806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063494666b614610030575b600080fd5b61004361003e36600461023b565b610045565b005b61006673ffffffffffffffffffffffffffffffffffffffff8316338361006a565b5050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052906000906100fd90861683610179565b90506101088161018e565b610172576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a207472616e73666572206661696c6564000000000000604482015260640160405180910390fd5b5050505050565b6060610187836000846101b5565b9392505050565b60008151600014806101af5750818060200190518101906101af9190610280565b92915050565b606060008473ffffffffffffffffffffffffffffffffffffffff1684846040516101df91906102a2565b60006040518083038185875af1925050503d806000811461021c576040519150601f19603f3d011682016040523d82523d6000602084013e610221565b606091505b50925090508061023357815160208301fd5b509392505050565b6000806040838503121561024e57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff8116811461027257600080fd5b946020939093013593505050565b60006020828403121561029257600080fd5b8151801515811461018757600080fd5b6000825160005b818110156102c357602081860181015185830152016102a9565b50600092019182525091905056fea164736f6c6343000811000a","deployedBytecode":"0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063494666b614610030575b600080fd5b61004361003e36600461023b565b610045565b005b61006673ffffffffffffffffffffffffffffffffffffffff8316338361006a565b5050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052906000906100fd90861683610179565b90506101088161018e565b610172576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a207472616e73666572206661696c6564000000000000604482015260640160405180910390fd5b5050505050565b6060610187836000846101b5565b9392505050565b60008151600014806101af5750818060200190518101906101af9190610280565b92915050565b606060008473ffffffffffffffffffffffffffffffffffffffff1684846040516101df91906102a2565b60006040518083038185875af1925050503d806000811461021c576040519150601f19603f3d011682016040523d82523d6000602084013e610221565b606091505b50925090508061023357815160208301fd5b509392505050565b6000806040838503121561024e57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff8116811461027257600080fd5b946020939093013593505050565b60006020828403121561029257600080fd5b8151801515811461018757600080fd5b6000825160005b818110156102c357602081860181015185830152016102a9565b50600092019182525091905056fea164736f6c6343000811000a","devdoc":{"methods":{}},"userdoc":{"methods":{}}}
2 changes: 1 addition & 1 deletion crates/contracts/artifacts/Swapper.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/contracts/artifacts/Trader.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion crates/contracts/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,9 @@ fn main() {
generate_contract("CowAmmUniswapV2PriceOracle");

// Support contracts used for trade and token simulations.
generate_contract("Trader");
generate_contract("Solver");
generate_contract("Spardose");
generate_contract("Trader");

// Support contracts used for various order simulations.
generate_contract("Balances");
Expand Down
1 change: 1 addition & 0 deletions crates/contracts/solidity/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ CONTRACTS := \
Signatures.sol \
SimulateCode.sol \
Solver.sol \
Spardose.sol \
Swapper.sol \
Trader.sol
ARTIFACTS := $(patsubst %.sol,$(ARTIFACTDIR)/%.json,$(CONTRACTS))
Expand Down
30 changes: 19 additions & 11 deletions crates/contracts/solidity/Solver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import { Trader } from "./Trader.sol";
contract Solver {
using Caller for *;
using Math for *;
using SafeERC20 for *;

struct Mock {
bool enabled;
address spardose;
}

uint256 private _simulationOverhead;
uint256[] private _queriedBalances;
Expand All @@ -35,10 +39,10 @@ contract Solver {
/// @param tokens - list of tokens used in the trade
/// @param receiver - address receiving the bought tokens
/// @param settlementCall - the calldata of the `settle()` call
/// @param mockPreconditions - controls whether things like ETH wrapping
/// or setting allowance should be done on behalf of the
/// user to support quote verification even if the user didn't
/// wrap their ETH or set the necessary allowances yet.
/// @param mock - mocking configuration for the simulation; this controls
/// whether things like ETH wrapping, setting allowance and
/// pre-funding should be done on behalf of the user to support
/// quote verification for users who aren't ready to swap.
///
/// @return gasUsed - gas used for the `settle()` call
/// @return queriedBalances - list of balances stored during the simulation
Expand All @@ -51,30 +55,34 @@ contract Solver {
address[] calldata tokens,
address payable receiver,
bytes calldata settlementCall,
bool mockPreconditions
Mock memory mock
) external returns (
uint256 gasUsed,
uint256[] memory queriedBalances
) {
require(msg.sender == address(this), "only simulation logic is allowed to call 'swap' function");

// Prepare the trade in the context of the trader so we are allowed
// to set approvals and things like that.
if (mockPreconditions) {
if (mock.enabled) {
// Prepare the trade in the context of the trader so we are allowed
// to set approvals and things like that.
Trader(trader)
.prepareSwap(
settlementContract,
sellToken,
sellAmount,
nativeToken
nativeToken,
mock.spardose
);
}

// Warm the storage for sending ETH to smart contract addresses.
// We allow this call to revert becaues it was either unnecessary in the first place
// or failing to send `ETH` to the `receiver` will cause a revert in the settlement
// contract.
receiver.call{value: 0}("");
{
(bool success,) = receiver.call{value: 0}("");
success;
}

// Store pre-settlement balances
_storeSettlementBalances(tokens, settlementContract);
Expand Down
23 changes: 23 additions & 0 deletions crates/contracts/solidity/Spardose.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IERC20 } from "./interfaces/IERC20.sol";
import { SafeERC20 } from "./libraries/SafeERC20.sol";

/// @title A piggy bank contract (Spardose is piggy bank in German)
/// @notice This contract account is used for pre-funding traders with tokens
/// for quote simulations. A separate contract is used (instead of overriding
/// the balance of the solver or trader directly) in order to interfere as
/// little as possible with the settlement.
contract Spardose {
using SafeERC20 for *;

/// @dev Request funds from the piggy bank to be transferred to the caller.
/// Reverts if the transfer fails.
///
/// @param token - the token request funds for
/// @param amount - the amount of `token` to transfer
function requestFunds(address token, uint256 amount) external {
IERC20(token).safeTransfer(msg.sender, amount);
}
}
37 changes: 28 additions & 9 deletions crates/contracts/solidity/Trader.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Interaction, Trade, ISettlement } from "./interfaces/ISettlement.sol";
import { Caller } from "./libraries/Caller.sol";
import { Math } from "./libraries/Math.sol";
import { SafeERC20 } from "./libraries/SafeERC20.sol";
import { Spardose } from "./Spardose.sol";

/// @title A contract for impersonating a trader.
contract Trader {
Expand Down Expand Up @@ -62,29 +63,36 @@ contract Trader {
receive() external payable {}

/// @dev Executes needed actions on behalf of the trader to make the trade possible.
/// (e.g. wrapping ETH and setting approvals)
/// (e.g. wrapping ETH, setting approvals, and funding the account)
/// @param settlementContract - pass in settlement contract because it does not have
/// a stable address in tests.
/// @param sellToken - token being sold by the trade
/// @param sellAmount - expected amount to be sold according to the quote
/// @param nativeToken - ERC20 version of the chain's native token
/// @param spardose - piggy bank for requesting additional funds
function prepareSwap(
ISettlement settlementContract,
address sellToken,
uint256 sellAmount,
address nativeToken
address nativeToken,
address spardose
) external {
require(!alreadyCalled(), "prepareSwap can only be called once");

if (sellToken == nativeToken) {
uint256 availableNativeToken = IERC20(sellToken).balanceOf(address(this));
if (availableNativeToken < sellAmount) {
uint256 amountToWrap = sellAmount - availableNativeToken;
require(address(this).balance >= amountToWrap, "not enough ETH to wrap");
// Simulate wrapping the missing `ETH` so the user doesn't have to spend gas
// on that just to get a quote. If they are happy with the quote and want to
// create an order they will actually have to do the wrapping, though.
INativeERC20(nativeToken).deposit{value: amountToWrap}();
// If the user has sufficient balance, simulate the wrapping the missing
// `ETH` so the user doesn't have to spend gas on that just to get a quote.
// If they are happy with the quote and want to create an order they will
// actually have to do the wrapping, though. Note that we don't attempt to
// wrap if the user doesn't have sufficient `ETH` balance, since that would
// revert. Instead, we fall-through so that we handle insufficient sell
// token balances uniformly for all tokens.
if (address(this).balance >= amountToWrap) {
INativeERC20(nativeToken).deposit{value: amountToWrap}();
}
}
}

Expand All @@ -100,8 +108,19 @@ contract Trader {
IERC20(sellToken).safeApprove(address(settlementContract.vaultRelayer()), type(uint256).max);
}

uint256 availableSellToken = IERC20(sellToken).balanceOf(address(this));
require(availableSellToken >= sellAmount, "trader does not have enough sell_token");
// Ensure that the user has sufficient sell token balance. If not, request some
// funds from the Spardose (piggy bank) which will be available if balance
// overrides are enabled.
uint256 sellBalance = IERC20(sellToken).balanceOf(address(this));
if (sellBalance < sellAmount) {
try Spardose(spardose).requestFunds(sellToken, sellAmount - sellBalance) {}
catch {
// The trader does not have sufficient sell token balance, and the
// piggy bank pre-fund failed, as balance overrides are not available.
// Revert with a helpful message.
revert("trader does not have enough sell token");
}
}
}

/// @dev Validate all signature requests. This makes "signing" CoW protocol
Expand Down
12 changes: 9 additions & 3 deletions crates/contracts/solidity/libraries/SafeERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ import { Caller } from "./Caller.sol";
library SafeERC20 {
using Caller for *;

function safeTransfer(IERC20 self, address target, uint256 amount) internal {
bytes memory cdata = abi.encodeCall(self.transfer, (target, amount));
bytes memory rdata = address(self).doCall(cdata);
require(check(rdata), "SafeERC20: transfer failed");
}

function safeApprove(IERC20 self, address target, uint256 amount) internal {
bytes memory cdata = abi.encodeCall(self.approve, (target, amount));
bytes memory rdata = address(self).doCall(cdata);
check(rdata, "SafeERC20: approval failed");
require(check(rdata), "SafeERC20: approval failed");
}

function check(bytes memory self, string memory message) internal pure {
require(self.length == 0 || abi.decode(self, (bool)), message);
function check(bytes memory rdata) internal pure returns (bool ok) {
return rdata.length == 0 || abi.decode(rdata, (bool));
}
}
1 change: 1 addition & 0 deletions crates/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub mod support {
Signatures;
SimulateCode;
Solver;
Spardose;
Swapper;
Trader;
}
Expand Down
Loading

0 comments on commit 9df6582

Please sign in to comment.