Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
0xrs committed Mar 9, 2021
1 parent f16e479 commit 02bbedb
Show file tree
Hide file tree
Showing 11 changed files with 2,479 additions and 23 deletions.
4 changes: 2 additions & 2 deletions contracts/Greeter.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.7.0;
pragma solidity ^0.6.9;

import "hardhat/console.sol";


contract Greeter {
string greeting;

constructor(string memory _greeting) {
constructor(string memory _greeting) public {
console.log("Deploying a Greeter with greeting:", _greeting);
greeting = _greeting;
}
Expand Down
107 changes: 107 additions & 0 deletions contracts/MetaExchange.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.6.9;
pragma experimental ABIEncoderV2;

import "hardhat/console.sol";

import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { ERC721 } from '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import { VerifySignature } from './utils/VerifySignature.sol';

contract MetaExchange is VerifySignature {
mapping (bytes32 => bool) public fills;
mapping (address => mapping(uint256 => bool)) public nonces;
/* event Filled(address indexed makerAddress, uint makerAmount, address indexed makerToken, address takerAddress, uint takerAmount, address indexed takerToken, uint256 expiration, uint256 nonce); */
/* event Canceled(address indexed makerAddress, uint makerAmount, address indexed makerToken, address takerAddress, uint takerAmount, address indexed takerToken, uint256 expiration, uint256 nonce); */

/** Event thrown when a trade fails
* Error codes:
* 1 -> 'The makeAddress and takerAddress must be different',
* 2 -> 'The order has expired',
* 3 -> 'This order has already been filled',
* 4 -> 'The ether sent with this transaction does not match takerAmount',
* 5 -> 'No ether is required for a trade between tokens',
* 6 -> 'The sender of this transaction must match the takerAddress',
* 7 -> 'Order has already been cancelled or filled'
*/
/* event Failed(uint code, address indexed makerAddress, uint makerAmount, address indexed makerToken, address takerAddress, uint takerAmount, address indexed takerToken, uint256 expiration, uint256 nonce); */

function fill(address makerAddress,
address takerAddress,
address[] memory makerErc20Addresses,
uint256[] memory makerErc20Amounts,
address[] memory makerErc721Addresses,
uint256[] memory makerErc721Amounts,
//address[] memory makerErc1155Addresses,
//uint256[] memory makerErc1155Amounts,
address[] memory takerErc20Addresses,
uint256[] memory takerErc20Amounts,
address[] memory takerErc721Addresses,
uint256[] memory takerErc721Amounts,
//address[] memory takerErc1155Addresses,
//uint256[] memory takerErc1155Amounts,
uint256 expiration,
uint256 nonce,
bytes memory signedMsg
//uint8 v,
//bytes32 r,
//bytes32 s
)
public {

if (makerAddress == takerAddress) {
/* msg.sender.transfer(msg.value);
Failed(1,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce); */
return;
}

if (expiration < now) {
/* msg.sender.transfer(msg.value);
Failed(2,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce); */
return;
}
// Validate the message by signature.
(bytes32 hash, bool validSig) = verify(makerAddress, makerAddress, takerAddress, makerErc20Addresses,
makerErc20Amounts, takerErc20Addresses, takerErc20Amounts,
expiration, nonce, signedMsg);
require(validSig == true, "Signature not valid");

if (fills[hash]) {
/* msg.sender.transfer(msg.value);
Failed(3,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce); */
return;
}

uint i;

//trade erc20s
for (i=0; i<makerErc20Addresses.length; i++) {
IERC20(makerErc20Addresses[i]).transferFrom(makerAddress, takerAddress, makerErc20Amounts[i]);
}

for (i=0; i<takerErc20Addresses.length; i++) {
IERC20(takerErc20Addresses[i]).transferFrom(takerAddress, makerAddress, takerErc20Amounts[i]);
}

//trade erc721s

//trade erc1155s


}

function cancel() public {

}

}
Empty file added contracts/mock/MockERC1155.sol
Empty file.
16 changes: 16 additions & 0 deletions contracts/mock/MockERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.6.9;
pragma experimental ABIEncoderV2;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract MockERC20 is ERC20, Ownable {
constructor (string memory _name, string memory _symbol) public ERC20(_name, _symbol) {

}

function mint(address _to, uint256 _amount) public onlyOwner {
_mint(_to, _amount);
}
}
16 changes: 16 additions & 0 deletions contracts/mock/MockERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.6.9;
pragma experimental ABIEncoderV2;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC721 } from '@openzeppelin/contracts/token/ERC721/ERC721.sol';

contract MockERC721 is ERC721, Ownable {
constructor (string memory _name, string memory _symbol) public ERC721(_name, _symbol) {

}

function mint(address _to, uint256 _tokenId) public onlyOwner {
_mint(_to, _tokenId);
}
}
213 changes: 213 additions & 0 deletions contracts/utils/Airswap
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
pragma solidity ^0.4.11;

import {StandardToken as ERC20} from "./lib/StandardToken.sol";

/** @title AirSwap exchange contract.
* Assumes makers and takers have approved this contract to access their balances.
*/
contract Exchange {

// Mapping of order hash to bool (true = already filled).
mapping (bytes32 => bool) public fills;

// Events that are emitted in different scenarios.
event Filled(address indexed makerAddress, uint makerAmount, address indexed makerToken, address takerAddress, uint takerAmount, address indexed takerToken, uint256 expiration, uint256 nonce);
event Canceled(address indexed makerAddress, uint makerAmount, address indexed makerToken, address takerAddress, uint takerAmount, address indexed takerToken, uint256 expiration, uint256 nonce);

/** Event thrown when a trade fails
* Error codes:
* 1 -> 'The makeAddress and takerAddress must be different',
* 2 -> 'The order has expired',
* 3 -> 'This order has already been filled',
* 4 -> 'The ether sent with this transaction does not match takerAmount',
* 5 -> 'No ether is required for a trade between tokens',
* 6 -> 'The sender of this transaction must match the takerAddress',
* 7 -> 'Order has already been cancelled or filled'
*/
event Failed(uint code, address indexed makerAddress, uint makerAmount, address indexed makerToken, address takerAddress, uint takerAmount, address indexed takerToken, uint256 expiration, uint256 nonce);

/** Fills an order by transferring tokens between (maker or escrow) and taker.
* maker is given tokenA to taker,
*/
function fill(address makerAddress, uint makerAmount, address makerToken,
address takerAddress, uint takerAmount, address takerToken,
uint256 expiration, uint256 nonce, uint8 v, bytes32 r, bytes32 s) payable {

if (makerAddress == takerAddress) {
msg.sender.transfer(msg.value);
Failed(1,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
return;
}

// Check if this order has expired
if (expiration < now) {
msg.sender.transfer(msg.value);
Failed(2,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
return;
}

// Validate the message by signature.
bytes32 hash = validate(makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce, v, r, s);

// Check if this order has already been filled
if (fills[hash]) {
msg.sender.transfer(msg.value);
Failed(3,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
return;
}

// Check to see if this an order for ether.
if (takerToken == address(0x0)) {

// Check to make sure the message value is the order amount.
if (msg.value == takerAmount) {

// Mark order as filled to prevent reentrancy.
fills[hash] = true;

// Perform the trade between makerAddress and takerAddress.
// The transfer will throw if there's a problem.
assert(transfer(makerAddress, takerAddress, makerAmount, makerToken));

// Transfer the ether received from sender to makerAddress.
makerAddress.transfer(msg.value);

// Log an event to indicate completion.
Filled(makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);

} else {
msg.sender.transfer(msg.value);
Failed(4,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
}

} else {
// This is an order trading two tokens
// Check that no ether has been sent accidentally
if (msg.value != 0) {
msg.sender.transfer(msg.value);
Failed(5,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
return;
}

if (takerAddress == msg.sender) {

// Mark order as filled to prevent reentrancy.
fills[hash] = true;

// Perform the trade between makerAddress and takerAddress.
// The transfer will throw if there's a problem.
// Assert should never fail
assert(trade(makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken));

// Log an event to indicate completion.
Filled(
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);

} else {
Failed(6,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
}
}
}

/** Cancels an order by refunding escrow and adding it to the fills mapping.
* Will log an event if
* - order has been cancelled or
* - order has already been filled
* and will do nothing if the maker of the order in question is not the
* msg.sender
*/
function cancel(address makerAddress, uint makerAmount, address makerToken,
address takerAddress, uint takerAmount, address takerToken,
uint256 expiration, uint256 nonce, uint8 v, bytes32 r, bytes32 s) {

// Validate the message by signature.
bytes32 hash = validate(makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce, v, r, s);

// Only the maker can cancel an order
if (msg.sender == makerAddress) {

// Check that order has not already been filled/cancelled
if (fills[hash] == false) {

// Cancel the order by considering it filled.
fills[hash] = true;

// Broadcast an event to the blockchain.
Canceled(makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);

} else {
Failed(7,
makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);
}
}
}

/** Atomic trade of tokens between first party and second party.
* Throws if one of the trades does not go through.
*/
function trade(address makerAddress, uint makerAmount, address makerToken,
address takerAddress, uint takerAmount, address takerToken) private returns (bool) {
return (transfer(makerAddress, takerAddress, makerAmount, makerToken) &&
transfer(takerAddress, makerAddress, takerAmount, takerToken));
}

/** Transfers tokens from first party to second party.
* Prior to a transfer being done by the contract, ensure that
* tokenVal.approve(this, amount, {from : address}) has been called
* throws if the transferFrom of the token returns false
* returns true if, the transfer went through
*/
function transfer(address from, address to, uint amount, address token) private returns (bool) {
require(ERC20(token).transferFrom(from, to, amount));
return true;
}

/** Validates order arguments for fill() and cancel() functions. */
function validate(address makerAddress, uint makerAmount, address makerToken,
address takerAddress, uint takerAmount, address takerToken,
uint256 expiration, uint256 nonce, uint8 v, bytes32 r, bytes32 s) private returns (bytes32) {

// Hash arguments to identify the order.
bytes32 hashV = keccak256(makerAddress, makerAmount, makerToken,
takerAddress, takerAmount, takerToken,
expiration, nonce);

bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = sha3(prefix, hashV);

require(ecrecover(prefixedHash, v, r, s) == makerAddress);

return hashV;
}
}
Loading

0 comments on commit 02bbedb

Please sign in to comment.