Skip to content
202 changes: 202 additions & 0 deletions contracts/integrations/airswap/AirswapFeeConnector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
pragma solidity 0.5.17;

import "../../openzeppelin/SafeMath.sol";
import "../../openzeppelin/PausableOz.sol";
import "../../openzeppelin/IERC20_.sol";

import "./IAirswapFeeConnector.sol";
import "./IAirswapSwapERC20.sol";

contract AirswapFeeConnector is PausableOz, IAirswapFeeConnector {
using SafeMath for uint256;

struct SwapRequest {
address sender;
address recipient;
uint256 nonce;
uint256 expiry;
address signerWallet;
address signerToken;
uint256 signerAmount;
address senderToken;
uint256 totalSenderAmount;
uint8 v;
bytes32 r;
bytes32 s;
}

uint256 public constant POINTS = 1000;

uint256 public inputFeeInPoints = 0;
uint256 public outputFeeInPoints = 0;

address public feeVaultAddress = address(0);
address public swapERC20Address = address(0);

event FeeVaultAddressChangedEvent(address indexed sender, address newAddress);
event SwapERC20AddressChangedEvent(address indexed sender, address newAddress);
event InputFeeChangedEvent(address indexed sender, uint256 feeInPoints);
event OutputFeeChangedEvent(address indexed sender, uint256 feeInPoints);

event SwapEvent(
address indexed sender,
address indexed recipient,
address sendToken,
uint256 sendAmount,
uint256 inputFee,
address receiveToken,
uint256 receiveAmount,
uint256 outputFee
);

/// @notice Set the input fee in points, ie 25 means 2.5 percent.
/// The input fee is collected on the sent tokens before
/// the actual conversion.
/// @param _inputFeeInPoints The new fee in points
function setInputFee(uint256 _inputFeeInPoints) public onlyOwner {
inputFeeInPoints = _inputFeeInPoints;
emit InputFeeChangedEvent(msg.sender, inputFeeInPoints);
}

/// @notice Set the output fee in points, ie 25 means 2.5 percent.
/// The output fee is collected after the conversion.
/// @param _outputFeeInPoints The new fee in points
function setOutputFee(uint256 _outputFeeInPoints) public onlyOwner {
outputFeeInPoints = _outputFeeInPoints;
emit OutputFeeChangedEvent(msg.sender, outputFeeInPoints);
}

/// @notice Set the address to which fees are sent
/// @param _newAddress The new address
function setFeeVaultAddress(address _newAddress) public onlyOwner {
feeVaultAddress = _newAddress;
require(feeVaultAddress != address(0), "invalid vault");
emit FeeVaultAddressChangedEvent(msg.sender, feeVaultAddress);
}

/// @notice Set the address of the AirSwap contract
/// @param _newAddress The new address
function setSwapERC20Address(address _newAddress) public onlyOwner {
swapERC20Address = _newAddress;
require(swapERC20Address != address(0), "invalid swapper");
emit SwapERC20AddressChangedEvent(msg.sender, swapERC20Address);
}

function calculateInputFee(uint256 _sendAmount) public view returns (uint256) {
return _sendAmount.mul(inputFeeInPoints).div(POINTS);
}

function calculateOutputFee(uint256 _receiveAmount) public view returns (uint256) {
return _receiveAmount.mul(outputFeeInPoints).div(POINTS);
}

/// @notice Swap one token for another.
/// @param _sender Address which is sending the tokens
/// @param _recipient Address to send the resulting tokens after collecting the output fee
/// @param _nonce A one time nonce
/// @param _expiry Date at which the original proposal will expire
/// @param _signerWallet Address of the market maker wallet
/// @param _signerToken Address of the token to convert to
/// @param _signerAmount Amount of resulting token from the conversion
/// @param _totalSenderAmount The amount to be sent before the input fee is collected.
/// @param _v v part of the ECDSA signature
/// @param _r r part of the ECDSA signature
/// @param _s s part of the ECDSA signature
function swap(
address _sender,
address _recipient,
uint256 _nonce,
uint256 _expiry,
address _signerWallet,
address _signerToken,
uint256 _signerAmount,
address _senderToken,
uint256 _totalSenderAmount,
uint8 _v,
bytes32 _r,
bytes32 _s
) public {
require(feeVaultAddress != address(0), "invalid vault");
require(swapERC20Address != address(0), "invalid swapper");

SwapRequest memory swapRequest =
SwapRequest(
_sender,
_recipient,
_nonce,
_expiry,
_signerWallet,
_signerToken,
_signerAmount,
_senderToken,
_totalSenderAmount,
_v,
_r,
_s
);

// first we move all the funds here
require(
IERC20_(swapRequest.senderToken).transferFrom(
swapRequest.sender,
address(this),
swapRequest.totalSenderAmount
),
"transfer failed 1"
);

// then we collect the input fee
uint256 inputFee = calculateInputFee(swapRequest.totalSenderAmount);
require(
IERC20_(swapRequest.senderToken).transfer(feeVaultAddress, inputFee),
"transfer failed 2"
);

uint256 senderAmountAfterFee = swapRequest.totalSenderAmount.sub(inputFee);

// now we do the swap
IAirswapSwapERC20(swapERC20Address).swap(
address(this),
swapRequest.nonce,
swapRequest.expiry,
swapRequest.signerWallet,
swapRequest.signerToken,
swapRequest.signerAmount,
swapRequest.senderToken,
senderAmountAfterFee,
swapRequest.v,
swapRequest.r,
swapRequest.s
);

// now we collect the output fee
uint256 outputFee = calculateOutputFee(swapRequest.signerAmount);
require(
IERC20_(swapRequest.signerToken).transfer(feeVaultAddress, outputFee),
"transfer failed 3"
);

uint256 receiveAmountAfterFee = swapRequest.signerAmount.sub(outputFee);

// now we send the user her due
require(
IERC20_(swapRequest.signerToken).transfer(
swapRequest.recipient,
receiveAmountAfterFee
),
"transfer failed 4"
);

// emit the event
emit SwapEvent(
swapRequest.sender,
swapRequest.recipient,
swapRequest.senderToken,
swapRequest.totalSenderAmount,
inputFee,
swapRequest.signerToken,
receiveAmountAfterFee,
outputFee
);
}
}
143 changes: 143 additions & 0 deletions contracts/integrations/airswap/EnumerableMakerSet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
pragma solidity ^0.5.0;

import "./IAirswapFeeConnector.sol";
/**
* @dev Based on Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
*
* Include with `using EnumerableSet for EnumerableSet.AddressSet;`.
*
* _Available since v2.5.0._
*/
library EnumerableMakerSet {
struct MakerSet {
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(address => uint256) index;
IAirswapFeeConnector.Maker[] values;
}

/**
* @dev Add a value to a set. O(1).
* Returns false if the value was already in the set.
*/
function add(MakerSet storage set, IAirswapFeeConnector.Maker memory value) internal returns (bool) {
if (!contains(set, value.signer)) {
set.index[value.signer] = set.values.push(value);
return true;
} else {
return false;
}
}

/**
* @dev Removes a value from a set. O(1).
* Returns false if the value was not present in the set.
*/
function remove(MakerSet storage set, IAirswapFeeConnector.Maker memory value) internal returns (bool) {
if (contains(set, value.signer)) {
uint256 toDeleteIndex = set.index[value.signer] - 1;
uint256 lastIndex = set.values.length - 1;

// If the element we're deleting is the last one, we can just remove it without doing a swap
if (lastIndex != toDeleteIndex) {
IAirswapFeeConnector.Maker memory lastValue = set.values[lastIndex];

// Move the last value to the index where the deleted value is
set.values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set.index[lastValue.signer] = toDeleteIndex + 1; // All indexes are 1-based
}

// Delete the index entry for the deleted value
delete set.index[value.signer];

// Delete the old entry for the moved value
set.values.pop();

return true;
} else {
return false;
}
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(MakerSet storage set, address signer) internal view returns (bool) {
return set.index[signer] != 0;
}

/**
* @dev Returns an array with all values in the set. O(N).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.

* WARNING: This function may run out of gas on large sets: use {length} and
* {get} instead in these cases.
*/
function enumerate(MakerSet storage set) internal view returns (IAirswapFeeConnector.Maker[] memory) {
IAirswapFeeConnector.Maker[] memory output = new IAirswapFeeConnector.Maker[](set.values.length);
for (uint256 i; i < set.values.length; i++) {
output[i] = set.values[i];
}
return output;
}

/**
* @dev Returns a chunk of array as recommended in enumerate() to avoid running of gas.
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.

* WARNING: This function may run out of gas on large sets: use {length} and
* {get} instead in these cases.

* @param start start index of chunk
* @param count num of element to return; if count == 0 then returns all the elements from the @param start
*/
function enumerateChunk(
MakerSet storage set,
uint256 start,
uint256 count
) internal view returns (IAirswapFeeConnector.Maker[] memory output) {
uint256 end = start + count;
require(end >= start, "addition overflow");
end = (set.values.length < end || count == 0) ? set.values.length : end;
if (end == 0 || start >= end) {
return output;
}

output = new IAirswapFeeConnector.Maker[](end - start);
for (uint256 i; i < end - start; i++) {
output[i] = set.values[i + start];
}
return output;
}

/**
* @dev Returns the number of elements on the set. O(1).
*/
function length(MakerSet storage set) internal view returns (uint256) {
return set.values.length;
}

/** @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function get(MakerSet storage set, uint256 index) internal view returns (IAirswapFeeConnector.Maker memory) {
return set.values[index];
}
}
Loading