pragma solidity ^0.4.19;
//
// UberDelta Exchange Contract - v1.0.0
//
// www.uberdelta.com
//
contract Token {
function balanceOf(address _owner) public view returns (uint256 balance);
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
contract SafeMath {
function safeMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
if (a == 0) {
return 0;
}
c = a * b;
require(c / a == b);
return c;
}
function safeDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b > 0); //gentler than an assert.
c = a / b;
return c;
}
function safeSub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
return a - b;
}
function safeAdd(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
require(c >= a);
return c;
}
}
contract OwnerManager {
address public owner;
address public newOwner;
address public manager;
event OwnershipTransferProposed(address indexed _from, address indexed _to);
event OwnershipTransferConfirmed(address indexed _from, address indexed _to);
event NewManager(address indexed _newManager);
modifier onlyOwner {
assert(msg.sender == owner);
_;
}
modifier onlyManager {
assert(msg.sender == manager);
_;
}
function OwnerManager() public{
owner = msg.sender;
manager = msg.sender;
}
function transferOwnership(address _newOwner) onlyOwner external{
require(_newOwner != owner);
OwnershipTransferProposed(owner, _newOwner);
newOwner = _newOwner;
}
function confirmOwnership() external {
assert(msg.sender == newOwner);
OwnershipTransferConfirmed(owner, newOwner);
owner = newOwner;
}
function newManager(address _newManager) onlyOwner external{
require(_newManager != address(0x0));
NewManager(_newManager);
manager = _newManager;
}
}
contract Helper is OwnerManager {
mapping (address => bool) public isHelper;
modifier onlyHelper {
assert(isHelper[msg.sender] == true);
_;
}
event ChangeHelper(
address indexed helper,
bool status
);
function Helper() public{
isHelper[msg.sender] = true;
}
function changeHelper(address _helper, bool _status) external onlyManager {
ChangeHelper(_helper, _status);
isHelper[_helper] = _status;
}
}
contract Compliance {
function canDeposit(address _user) public view returns (bool isAllowed);
function canTrade(address _token, address _user) public view returns (bool isAllowed);
function validateTrade(
address _token,
address _getUser,
address _giveUser
)
public
view
returns (bool isAllowed)
;
}
contract OptionRegistry {
function registerOptionPair(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
)
public
returns(bool)
;
function isOptionPairRegistered(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
)
public
view
returns(bool)
;
}
contract EOS {
function register(string key) public;
}
contract UberDelta is SafeMath, OwnerManager, Helper {
// The account that will receive fees
address public feeAccount;
// The account that will receive lost ERC20 tokens
address public sweepAccount;
// The address of the compliance engine
address public complianceAddress;
// The address of the options registry
address public optionsRegistryAddress;
// The address of the next exchange contract
address public newExchange;
// Turn off deposits and trades, allow upgrade and withdraw
bool public contractLocked;
bytes32 signedTradeHash = keccak256(
"address contractAddress",
"address takerTokenAddress",
"uint256 takerTokenAmount",
"address makerTokenAddress",
"uint256 makerTokenAmount",
"uint256 tradeExpires",
"uint256 salt",
"address maker",
"address restrictedTo"
);
bytes32 signedWithdrawHash = keccak256(
"address contractAddress",
"uint256 amount",
"uint256 fee",
"uint256 withdrawExpires",
"uint256 salt",
"address maker",
"address restrictedTo"
);
// Balance per token, for each user.
mapping (address => mapping (address => uint256)) public balances;
// global token balance tracking (to detect lost tokens)
mapping (address => uint256) public globalBalance;
// List of orders created by calling the exchange contract directly.
mapping (bytes32 => bool) public orders;
// Lists the amount of each order that has been filled or cancelled.
mapping (bytes32 => uint256) public orderFills;
// Tokens that need to be checked through the compliance engine.
mapping (address => bool) public restrictedTokens;
// Mapping of fees by user class (default class == 0x0)
mapping (uint256 => uint256) public feeByClass;
// Mapping of users to user classes.
mapping (address => uint256) public userClass;
/*******************************************
/ Exchange Regular Events
/******************************************/
// Note: Order creation is usually off-chain
event Order(
bytes32 indexed tradePair,
address indexed maker,
address[4] addressData,
uint256[4] numberData
);
event Cancel(
bytes32 indexed tradePair,
address indexed maker,
address[4] addressData,
uint256[4] numberData,
uint256 status
);
event FailedTrade(
bytes32 indexed tradePair,
address indexed taker,
bytes32 hash,
uint256 failReason
);
event Trade(
bytes32 indexed tradePair,
address indexed maker,
address indexed taker,
address makerToken,
address takerToken,
address restrictedTo,
uint256[4] numberData,
uint256 tradeAmount,
bool fillOrKill
);
event Deposit(
address indexed token,
address indexed toUser,
address indexed sender,
uint256 amount
);
event Withdraw(
address indexed token,
address indexed toUser,
uint256 amount
);
event InternalTransfer(
address indexed token,
address indexed toUser,
address indexed sender,
uint256 amount
);
event TokenSweep(
address indexed token,
address indexed sweeper,
uint256 amount,
uint256 balance
);
event RestrictToken(
address indexed token,
bool status
);
event NewExchange(
address newExchange
);
event ChangeFeeAccount(
address feeAccount
);
event ChangeSweepAccount(
address sweepAccount
);
event ChangeClassFee(
uint256 indexed class,
uint256 fee
);
event ChangeUserClass(
address indexed user,
uint256 class
);
event LockContract(
bool status
);
event UpdateComplianceAddress(
address newComplianceAddress
);
event UpdateOptionsRegistryAddress(
address newOptionsRegistryAddress
);
event Upgrade(
address indexed user,
address indexed token,
address newExchange,
uint256 amount
);
event RemoteWithdraw(
address indexed maker,
address indexed sender,
uint256 withdrawAmount,
uint256 feeAmount,
uint256 withdrawExpires,
uint256 salt,
address restrictedTo
);
event CancelRemoteWithdraw(
address indexed maker,
uint256 withdrawAmount,
uint256 feeAmount,
uint256 withdrawExpires,
uint256 salt,
address restrictedTo,
uint256 status
);
//Constructor Function, set initial values.
function UberDelta() public {
feeAccount = owner;
sweepAccount = owner;
feeByClass[0x0] = 3000000000000000;
contractLocked = false;
complianceAddress = this;
optionsRegistryAddress = this;
}
// Prevent raw sends of Eth.
function() public {
revert();
}
/*******************************************
/ Contract Control Functions
/******************************************/
function changeNewExchange(address _newExchange) external onlyOwner {
//since _newExchange being zero turns off the upgrade function, lets
//allow this to be reset to 0x0.
newExchange = _newExchange;
NewExchange(_newExchange);
}
function changeFeeAccount(address _feeAccount) external onlyManager {
require(_feeAccount != address(0x0));
feeAccount = _feeAccount;
ChangeFeeAccount(_feeAccount);
}
function changeSweepAccount(address _sweepAccount) external onlyManager {
require(_sweepAccount != address(0x0));
sweepAccount = _sweepAccount;
ChangeSweepAccount(_sweepAccount);
}
function changeClassFee(uint256 _class, uint256 _fee) external onlyManager {
require(_fee <= 10000000000000000); //Max 1%.
feeByClass[_class] = _fee;
ChangeClassFee(_class, _fee);
}
function changeUserClass(address _user, uint256 _newClass) external onlyHelper {
userClass[_user] = _newClass;
ChangeUserClass(_user, _newClass);
}
//Turn off deposits and trades, but still allow withdrawals and upgrades.
function lockContract(bool _lock) external onlyManager {
contractLocked = _lock;
LockContract(_lock);
}
function updateComplianceAddress(address _newComplianceAddress)
external
onlyManager
{
complianceAddress = _newComplianceAddress;
UpdateComplianceAddress(_newComplianceAddress);
}
function updateOptionsRegistryAddress(address _newOptionsRegistryAddress)
external
onlyManager
{
optionsRegistryAddress = _newOptionsRegistryAddress;
UpdateOptionsRegistryAddress(_newOptionsRegistryAddress);
}
// restriction function for tokens that need additional verifications
function tokenRestriction(address _newToken, bool _status) external onlyHelper {
restrictedTokens[_newToken] = _status;
RestrictToken(_newToken, _status);
}
//Turn off deposits and trades, but still allow withdrawals and upgrades.
modifier notLocked() {
require(!contractLocked);
_;
}
/*******************************************************
/ Deposit/Withdrawal/Transfer
/
/ In all of the following functions, it should be noted
/ that the 0x0 address is used to represent ETH.
/******************************************************/
// SafeMath sanity checks inputs in deposit(), withdraw(), and token functions.
// Deposit ETH in the contract to trade with
function deposit() external notLocked payable returns(uint256) {
require(Compliance(complianceAddress).canDeposit(msg.sender));
// defaults to true until we change compliance code
balances[address(0x0)][msg.sender] = safeAdd(balances[address(0x0)][msg.sender], msg.value);
globalBalance[address(0x0)] = safeAdd(globalBalance[address(0x0)], msg.value);
Deposit(0x0, msg.sender, msg.sender, msg.value);
return(msg.value);
}
// Withdraw ETH from the contract to your wallet (internal transaction on etherscan)
function withdraw(uint256 _amount) external returns(uint256) {
//require(balances[address(0x0)][msg.sender] >= _amount);
//handled by safeSub.
balances[address(0x0)][msg.sender] = safeSub(balances[address(0x0)][msg.sender], _amount);
globalBalance[address(0x0)] = safeSub(globalBalance[address(0x0)], _amount);
//transfer has a built in require
msg.sender.transfer(_amount);
Withdraw(0x0, msg.sender, _amount);
return(_amount);
}
// Deposit ERC20 tokens in the contract to trade with
// Token(_token).approve(this, _amount) must be called in advance
// ERC223 tokens must be deposited by a transfer to this contract ( see tokenFallBack(..) )
function depositToken(address _token, uint256 _amount) external notLocked returns(uint256) {
require(_token != address(0x0));
require(Compliance(complianceAddress).canDeposit(msg.sender));
balances[_token][msg.sender] = safeAdd(balances[_token][msg.sender], _amount);
globalBalance[_token] = safeAdd(globalBalance[_token], _amount);
require(Token(_token).transferFrom(msg.sender, this, _amount));
Deposit(_token, msg.sender, msg.sender, _amount);
return(_amount);
}
// Withdraw ERC20/223 tokens from the contract back to your wallet
function withdrawToken(address _token, uint256 _amount)
external
returns (uint256)
{
if (_token == address(0x0)){
//keep the nulls to reduce gas usage.
//require(balances[_token)][msg.sender] >= _amount);
//handled by safeSub.
balances[address(0x0)][msg.sender] = safeSub(balances[address(0x0)][msg.sender], _amount);
globalBalance[address(0x0)] = safeSub(globalBalance[address(0x0)], _amount);
//transfer has a built in require
msg.sender.transfer(_amount);
} else {
//require(balances[_token][msg.sender] >= _amount);
//handled by safeSub
balances[_token][msg.sender] = safeSub(balances[_token][msg.sender], _amount);
globalBalance[_token] = safeSub(globalBalance[_token], _amount);
require(Token(_token).transfer(msg.sender, _amount));
}
Withdraw(_token, msg.sender, _amount);
return _amount;
}
// Deposit ETH in the contract on behalf of another address
// Warning: afterwards, only _user will be able to trade or withdraw these funds
function depositToUser(address _toUser) external payable notLocked returns (bool success) {
require(
(_toUser != address(0x0))
&& (_toUser != address(this))
&& (Compliance(complianceAddress).canDeposit(_toUser))
);
balances[address(0x0)][_toUser] = safeAdd(balances[address(0x0)][_toUser], msg.value);
globalBalance[address(0x0)] = safeAdd(globalBalance[address(0x0)], msg.value);
Deposit(0x0, _toUser, msg.sender, msg.value);
return true;
}
// Deposit ERC20 tokens in the contract on behalf of another address
// Token(_token).approve(this, _amount) must be called in advance
// Warning: afterwards, only _toUser will be able to trade or withdraw these funds
// ERC223 tokens must be deposited by a transfer to this contract ( see tokenFallBack(..) )
function depositTokenToUser(
address _toUser,
address _token,
uint256 _amount
)
external
notLocked
returns (bool success)
{
require(
(_token != address(0x0))
&& (_toUser != address(0x0))
&& (_toUser != address(this))
&& (_toUser != _token)
&& (Compliance(complianceAddress).canDeposit(_toUser))
);
balances[_token][_toUser] = safeAdd(balances[_token][_toUser], _amount);
globalBalance[_token] = safeAdd(globalBalance[_token], _amount);
require(Token(_token).transferFrom(msg.sender, this, _amount));
Deposit(_token, _toUser, msg.sender, _amount);
return true;
}
//ERC223 Token Acceptor function, called when an ERC2223 token is transferred to this contract
// provide _sendTo to make it a deposit on behalf of another address (depositToUser)
function tokenFallback(
address _from, // user calling the function
uint256 _value, // the number of tokens
bytes _sendTo // "deposit to other user" if exactly 20 bytes sent
)
external
notLocked
{
//first lets figure out who this is going to.
address toUser = _from; //probably this
if (_sendTo.length == 20){ //but use data for sendTo otherwise.
// I'm about 90% sure I don't need to do the casting here, but for
// like twenty gas, I'll take the protection from potentially
// stomping on weird memory locations.
uint256 asmAddress;
assembly { //uses 50 gas
asmAddress := calldataload(120)
}
toUser = address(asmAddress);
}
//sanity checks.
require(
(toUser != address(0x0))
&& (toUser != address(this))
&& (toUser != msg.sender) // msg.sender is the token
&& (Compliance(complianceAddress).canDeposit(toUser))
);
// check if a contract is calling this
uint256 codeLength;
assembly {
codeLength := extcodesize(caller)
}
require(codeLength > 0);
globalBalance[msg.sender] = safeAdd(globalBalance[msg.sender], _value);
balances[msg.sender][toUser] = safeAdd(balances[msg.sender][toUser], _value);
//sanity check, and as a perk, we check for balanceOf();
require(Token(msg.sender).balanceOf(this) >= _value);
Deposit(msg.sender, toUser, _from, _value);
}
// Move deposited tokens or ETH (0x0) from one to another address within the contract
function internalTransfer(
address _toUser,
address _token,
uint256 _amount
)
external
notLocked
returns(uint256)
{
require(
(balances[_token][msg.sender] >= _amount)
&& (_toUser != address(0x0))
&& (_toUser != address(this))
&& (_toUser != _token)
&& (Compliance(complianceAddress).canDeposit(_toUser))
);
balances[_token][msg.sender] = safeSub(balances[_token][msg.sender], _amount);
balances[_token][_toUser] = safeAdd(balances[_token][_toUser], _amount);
InternalTransfer(_token, _toUser, msg.sender, _amount);
return(_amount);
}
// return the token/ETH balance a user has deposited in the contract
function balanceOf(address _token, address _user) external view returns (uint) {
return balances[_token][_user];
}
// In order to see the ERC20 total balance, we're calling an external contract,
// and this contract claims to be ERC20, but we don't know what's really there.
// We can't rely on the EVM or solidity to enforce "view", so even though a
// normal token can rely on itself to be non-malicious, we can't.
// We have no idea what potentially evil tokens we'll be interacting with.
// The call to check the reported balance needs to go after the state changes,
// even though it's un-natural. Now, on one hand, this function might at first
// appear safe, since we're only allowing the sweeper address to access
// *this function,* but we are reading the state of the globalBalance.
// In theory, a malicious token could do the following:
// 1a) Check if the caller of balanceOf is our contract, if it's not, act normally.
// 1b) If the caller is our contract, it does the following:
// 2) Read our contracts globalBalance for its own address.
// 3) Sets our contract's balance of the token (in the token controller) to our internal globalBalance
// 4) Allocates some other address the difference in globalBalance and actual balance for our contract.
// 5) Report back to this function exactly the amount we had in globalBalance.
// (which, by then is true, since they were stolen).
// Now we're always going to see 0 extra tokens, and our users have had their tokens perminantly lost.
// bonus: this is why there is no "sweep all" function.
// Detect ERC20 tokens that have been sent to the contract without a deposit (lost tokens),
// which are not included in globalBalance[..]
function sweepTokenAmount(address _token, uint256 _amount) external returns(uint256) {
assert(msg.sender == sweepAccount);
balances[_token][sweepAccount] = safeAdd(balances[_token][sweepAccount], _amount);
globalBalance[_token] = safeAdd(globalBalance[_token], _amount);
//You go last!
if(_token != address(0x0)) {
require(globalBalance[_token] <= Token(_token).balanceOf(this));
} else {
// if another contract performs selfdestruct(UberDelta),
// ETH can get in here without being in globalBalance
require(globalBalance[address(0x0)] <= this.balance);
}
TokenSweep(_token, msg.sender, _amount, balances[_token][sweepAccount]);
return(_amount);
}
/*******************************************
/ Regular Trading functions
/******************************************/
//now contracts can place orders!
// Normal order creation happens off-chain and orders are signed by creators,
// this function allows for on-chain orders to be created
function order(
address[4] _addressData,
uint256[4] _numberData //web3 isn't ready for structs.
)
external
notLocked
returns (bool success)
{
// _addressData[2] is maker;
if (msg.sender != _addressData[2]) { return false; }
bytes32 hash = getHash(_addressData, _numberData);
orders[hash] = true;
Order(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
_addressData,
_numberData);
return true;
}
function tradeBalances(
address _takerTokenAddress,
uint256 _takerTokenAmount,
address _makerTokenAddress,
uint256 _makerTokenAmount,
address _maker,
uint256 _tradeAmount
)
internal
{
require(_takerTokenAmount > 0); //safeDiv
// We charge only the takers this fee
uint256 feeValue = safeMul(_tradeAmount, feeByClass[userClass[msg.sender]]) / (1 ether);
balances[_takerTokenAddress][_maker] =
safeAdd(balances[_takerTokenAddress][_maker], _tradeAmount);
balances[_takerTokenAddress][msg.sender] =
safeSub(balances[_takerTokenAddress][msg.sender], safeAdd(_tradeAmount, feeValue));
balances[_makerTokenAddress][_maker] =
safeSub(
balances[_makerTokenAddress][_maker],
safeMul(_makerTokenAmount, _tradeAmount) / _takerTokenAmount
);
balances[_makerTokenAddress][msg.sender] =
safeAdd(
balances[_makerTokenAddress][msg.sender],
safeMul(_makerTokenAmount, _tradeAmount) / _takerTokenAmount
);
balances[_takerTokenAddress][feeAccount] =
safeAdd(balances[_takerTokenAddress][feeAccount], feeValue);
}
function trade(
address[4] _addressData,
uint256[4] _numberData, //web3 isn't ready for structs.
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _amount,
bool _fillOrKill
)
external
notLocked
returns(uint256 tradeAmount)
{
// _addressData[0], // takerTokenAddress;
// _numberData[0], // takerTokenAmount;
// _addressData[1], // makerTokenAddress;
// _numberData[1], // makerTokenAmount;
// _numberData[2], // tradeExpires;
// _numberData[3], // salt;
// _addressData[2], // maker;
// _addressData[3] // restrictedTo;
bytes32 hash = getHash(_addressData, _numberData);
tradeAmount = safeSub(_numberData[0], orderFills[hash]); //avail to trade
//balance of giveToken / amount I said I'd give of giveToken * amount I said I want of getToken
if (
tradeAmount > safeDiv(
safeMul(balances[_addressData[1]][_addressData[2]], _numberData[0]),
_numberData[1]
)
)
{
tradeAmount = safeDiv(
safeMul(balances[_addressData[1]][_addressData[2]], _numberData[0]),
_numberData[1]
);
}
if (tradeAmount > _amount) { tradeAmount = _amount; }
//_numberData[0] is takerTokenAmount
if (tradeAmount == 0) { //idfk. There's nothing there to get. Canceled? Traded?
if (orderFills[hash] < _numberData[0]) { //Maker seems to be missing tokens?
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
0
);
} else { // either cancelled or already traded.
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
1
);
}
return 0;
}
if (block.number > _numberData[2]) { //order is expired
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
2
);
return 0;
}
if ((_fillOrKill == true) && (tradeAmount < _amount)) { //didnt fill, so kill
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
3
);
return 0;
}
uint256 feeValue = safeMul(_amount, feeByClass[userClass[msg.sender]]) / (1 ether);
//if they trade more than they have, get 0.
if ( (_amount + feeValue) > balances[_addressData[0]][msg.sender]) {
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
4
);
return 0;
}
if ( //not a valid order.
(ecrecover(keccak256(signedTradeHash, hash), _v, _r, _s) != _addressData[2])
&& (! orders[hash])
)
{
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
5
);
return 0;
}
if ((_addressData[3] != address(0x0)) && (_addressData[3] != msg.sender)) { //check restrictedTo
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
6
);
return 0;
}
if ( //if there's a compliance restriction.
((_addressData[0] != address(0x0)) //if not Eth, and restricted, check with Compliance.
&& (restrictedTokens[_addressData[0]] )
&& ! Compliance(complianceAddress).validateTrade(_addressData[0], _addressData[2], msg.sender)
)
|| ((_addressData[1] != address(0x0)) //ditto
&& (restrictedTokens[_addressData[1]])
&& ! Compliance(complianceAddress).validateTrade(_addressData[1], _addressData[2], msg.sender)
)
)
{
FailedTrade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
hash,
7
);
return 0;
}
//Do the thing!
tradeBalances(
_addressData[0], // takerTokenAddress;
_numberData[0], // takerTokenAmount;
_addressData[1], // makerTokenAddress;
_numberData[1], // makerTokenAmount;
_addressData[2], // maker;
tradeAmount
);
orderFills[hash] = safeAdd(orderFills[hash], tradeAmount);
Trade(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
_addressData[2],
msg.sender,
_addressData[1],
_addressData[0],
_addressData[3],
_numberData,
tradeAmount,
_fillOrKill
);
return(tradeAmount);
}
// Cancel a signed order, once this is confirmed nobody will be able to trade it anymore
function cancelOrder(
address[4] _addressData,
uint256[4] _numberData //web3 isn't ready for structs.
)
external
returns(uint256 amountCancelled)
{
require(msg.sender == _addressData[2]);
// msg.sender can 'cancel' nonexistent orders since they're offchain.
bytes32 hash = getHash(_addressData, _numberData);
amountCancelled = safeSub(_numberData[0],orderFills[hash]);
orderFills[hash] = _numberData[0];
//event trigger is moved ahead of balance resetting to allow expression of the already-filled amount
// _numberData[0] is takerTokenAmount;
Cancel(
(bytes32(_addressData[0]) ^ bytes32(_addressData[1])),
msg.sender,
_addressData,
_numberData,
amountCancelled);
return amountCancelled;
}
/**************************
/ Remote Withdraw
***************************/
// Perform an ETH withdraw transaction for someone else based on their signed message
// Useful if the owner of the funds does not have enough ETH for gas fees in their wallet.
// msg.sender receives fee for the effort and gas costs
function remoteWithdraw(
uint256 _withdrawAmount,
uint256 _feeAmount,
uint256 _withdrawExpires,
uint256 _salt,
address _maker,
address _restrictedTo, //0x0 = anyone
uint8 _v,
bytes32 _r,
bytes32 _s
)
external
notLocked
returns(bool)
{
//is the withdraw possible?
require(
(balances[address(0x0)][_maker] >= safeAdd(_withdrawAmount, _feeAmount))
&& (
(_restrictedTo == address(0x0))
|| (_restrictedTo == msg.sender)
)
&& ((_feeAmount == 0) || (Compliance(complianceAddress).canDeposit(msg.sender)))
);
//has this withdraw happened already? (and generate the hash)
bytes32 hash = keccak256(
this,
_withdrawAmount,
_feeAmount,
_withdrawExpires,
_salt,
_maker,
_restrictedTo
);
require(orderFills[hash] == 0);
//is this real?
require(
ecrecover(keccak256(signedWithdrawHash, hash), _v, _r, _s) == _maker
);
//only once.
orderFills[hash] = 1;
balances[address(0x0)][_maker] =
safeSub(balances[address(0x0)][_maker], safeAdd(_withdrawAmount, _feeAmount));
// pay fee to the user performing the remote withdraw
balances[address(0x0)][msg.sender] = safeAdd(balances[address(0x0)][msg.sender], _feeAmount);
globalBalance[address(0x0)] = safeSub(globalBalance[address(0x0)], _withdrawAmount);
RemoteWithdraw(
_maker,
msg.sender,
_withdrawAmount,
_feeAmount,
_withdrawExpires,
_salt,
_restrictedTo
);
//implicit require included.
_maker.transfer(_withdrawAmount);
return(true);
}
// cancel a signed request for a remote withdraw
function cancelRemoteWithdraw(
uint256 _withdrawAmount,
uint256 _feeAmount,
uint256 _withdrawExpires,
uint256 _salt,
address _restrictedTo //0x0 = anyone
)
external
{
// msg.sender can cancel nonexsistent orders.
bytes32 hash = keccak256(
this,
_withdrawAmount,
_feeAmount,
_withdrawExpires,
_salt,
msg.sender,
_restrictedTo
);
CancelRemoteWithdraw(
msg.sender,
_withdrawAmount,
_feeAmount,
_withdrawExpires,
_salt,
_restrictedTo,
orderFills[hash]
);
//set to completed after, event shows pre-cancel status.
orderFills[hash] = 1;
}
/**************************
/Upgrade Function
***************************/
// move tokens/ETH over to a new upgraded smart contract (avoids having to withdraw & deposit)
function upgrade(address _token) external returns(uint256 moveBalance) {
require (newExchange != address(0x0));
moveBalance = balances[_token][msg.sender];
globalBalance[_token] = safeSub(globalBalance[_token], moveBalance);
balances[_token][msg.sender] = 0;
if (_token != address(0x0)){
require(Token(_token).approve(newExchange, moveBalance));
require(UberDelta(newExchange).depositTokenToUser(msg.sender, _token, moveBalance));
} else {
require(UberDelta(newExchange).depositToUser.value(moveBalance)(msg.sender));
}
Upgrade(msg.sender, _token, newExchange, moveBalance);
return(moveBalance);
}
/*******************************************
/ Data View functions
/******************************************/
function testTrade(
address[4] _addressData,
uint256[4] _numberData, //web3 isn't ready for structs.
uint8 _v,
bytes32 _r,
bytes32 _s,
uint256 _amount,
address _sender,
bool _fillOrKill
)
public
view
returns(uint256)
{
uint256 feeValue = safeMul(_amount, feeByClass[userClass[_sender]]) / (1 ether);
if (
contractLocked
||
((_addressData[0] != address(0x0)) //if not Eth, and restricted, check with Compliance.
&& (restrictedTokens[_addressData[0]] )
&& ! Compliance(complianceAddress).validateTrade(_addressData[0], _addressData[2], _sender)
)
|| ((_addressData[1] != address(0x0)) //ditto
&& (restrictedTokens[_addressData[1]])
&& ! Compliance(complianceAddress).validateTrade(_addressData[1], _addressData[2], _sender)
)
//if they trade more than they have, get 0.
|| ((_amount + feeValue) > balances[_addressData[0]][_sender])
|| ((_addressData[3] != address(0x0)) && (_addressData[3] != _sender)) //check restrictedTo
)
{
return 0;
}
uint256 tradeAmount = availableVolume(
_addressData,
_numberData,
_v,
_r,
_s
);
if (tradeAmount > _amount) { tradeAmount = _amount; }
if ((_fillOrKill == true) && (tradeAmount < _amount)) {
return 0;
}
return tradeAmount;
}
// get how much of an order is left (unfilled)
// return value in order of _takerTokenAddress
function availableVolume(
address[4] _addressData,
uint256[4] _numberData, //web3 isn't ready for structs.
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
view
returns(uint256 amountRemaining)
{
// _addressData[0] // takerTokenAddress;
// _numberData[0] // takerTokenAmount;
// _addressData[1] // makerTokenAddress;
// _numberData[1] // makerTokenAmount;
// _numberData[2] // tradeExpires;
// _numberData[3] // salt;
// _addressData[2] // maker;
// _addressData[3] // restrictedTo;
bytes32 hash = getHash(_addressData, _numberData);
if (
(block.number > _numberData[2])
|| (
(ecrecover(keccak256(signedTradeHash, hash), _v, _r, _s) != _addressData[2])
&& (! orders[hash])
)
) { return 0; }
//uint256 amountRemaining = safeSub(myTrade.takerTokenAmount, orderFills[hash]);
amountRemaining = safeSub(_numberData[0], orderFills[hash]);
if (
amountRemaining < safeDiv(
safeMul(balances[_addressData[1]][_addressData[2]], _numberData[0]),
_numberData[1]
)
) return amountRemaining;
return (
safeDiv(
safeMul(balances[_addressData[1]][_addressData[2]], _numberData[0]),
_numberData[1]
)
);
}
// get how much of an order has been filled
// return value in order of _takerTokenAddress
function getUserFee(
address _user
)
external
view
returns(uint256)
{
return feeByClass[userClass[_user]];
}
// get how much of an order has been filled
// return value in order of _takerTokenAddress
function amountFilled(
address[4] _addressData,
uint256[4] _numberData //web3 isn't ready for structs.
)
external
view
returns(uint256)
{
bytes32 hash = getHash(_addressData, _numberData);
return orderFills[hash];
}
// check if a request for a remote withdraw is still valid
function testRemoteWithdraw(
uint256 _withdrawAmount,
uint256 _feeAmount,
uint256 _withdrawExpires,
uint256 _salt,
address _maker,
address _restrictedTo,
uint8 _v,
bytes32 _r,
bytes32 _s,
address _sender
)
external
view
returns(uint256)
{
bytes32 hash = keccak256(
this,
_withdrawAmount,
_feeAmount,
_withdrawExpires,
_salt,
_maker,
_restrictedTo
);
if (
contractLocked
||
(balances[address(0x0)][_maker] < safeAdd(_withdrawAmount, _feeAmount))
||((_restrictedTo != address(0x0)) && (_restrictedTo != _sender))
|| (orderFills[hash] != 0)
|| (ecrecover(keccak256(signedWithdrawHash, hash), _v, _r, _s) != _maker)
|| ((_feeAmount > 0) && (! Compliance(complianceAddress).canDeposit(_sender)))
)
{
return 0;
} else {
return _withdrawAmount;
}
}
function getHash(
address[4] _addressData,
uint256[4] _numberData //web3 isn't ready for structs.
)
public
view
returns(bytes32)
{
return(
keccak256(
this,
_addressData[0], // takerTokenAddress;
_numberData[0], // takerTokenAmount;
_addressData[1], // makerTokenAddress;
_numberData[1], // makerTokenAmount;
_numberData[2], // tradeExpires;
_numberData[3], // salt;
_addressData[2], // maker;
_addressData[3] // restrictedTo;
)
);
}
/***********************************
/ Compliance View Code
************************************/
//since the compliance code might move, we should have a way to always
//call a function to this contract to get the current values
function testCanDeposit(
address _user
)
external
view
returns (bool)
{
return(Compliance(complianceAddress).canDeposit(_user));
}
function testCanTrade(
address _token,
address _user
)
external
view
returns (bool)
{
return(Compliance(complianceAddress).canTrade(_token, _user));
}
function testValidateTrade(
address _token,
address _getUser,
address _giveUser
)
external
view
returns (bool isAllowed)
{
return(Compliance(complianceAddress).validateTrade(_token, _getUser, _giveUser));
}
/**************************
/ Default Compliance Code
***************************/
// These will eventually live in a different contract.
// every can deposit by default, later a registry?
// For now, always say no if called for trades.
// the earliest use may be halting trade in a token.
function canDeposit(
address _user
)
public
view
returns (bool isAllowed)
{
return(true);
}
function canTrade(
address _token,
address _user
)
public
view
returns (bool isAllowed)
{
return(false);
}
function validateTrade(
address _token,
address _getUser,
address _giveUser
)
public
view
returns (bool isAllowed)
{
return(false);
}
/***********************************
/ THIS IS WHERE OPTIONS LIVE!!!!
/**********************************/
mapping (address => uint256) public exercisedOptions;
//get asset for tickets
event CollapseOption(
address indexed user,
address indexed holderTicketAddress,
address indexed writerTicketAddress,
uint256 ticketsCollapsed,
bytes32 optionPair //assetTokenAddress xor strikeTokenAddress
);
//get holderticket + asset for strike
event ExcerciseUnwind(
address indexed user,
address indexed holderTicketAddress,
uint256 ticketsUnwound,
bytes32 optionPair,
bool fillOrKill
);
//get asset for writerticket
event ExpireOption(
address indexed user,
address indexed writerTicketAddress,
uint256 ticketsExpired,
bytes32 optionPair
);
//get tickets for asset
event CreateOption(
address indexed user,
address indexed holderTicketAddress,
address indexed writerTicketAddress,
uint256 ticketsCreated,
bytes32 optionPair
);
//get assset for strike + holderticket
event ExcerciseOption(
address indexed user,
address indexed holderTicketAddress,
uint256 ticketsExcercised,
bytes32 optionPair //assetTokenAddress xor strikeTokenAddress
);
/******************
/ optionFunctions
******************/
//if before expiry, deposit asset, get buy ticket, write ticket
// 1 ticket gets (10^18) option units credited to them.
function createOptionPair( //#65
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires,
uint256 _ticketAmount //tickets times (1 ether)
)
external
notLocked
returns (uint256 ticketsCreated)
{
//if before expiry
require (block.number < _optionExpires); //option would be expired
//if they have the asset
//[checked by safemath during locking]
//lock asset to 0x0.
//the percent of one contract times _assetTokenAmount = amount moving
//creation fee?
balances[_assetTokenAddress][0x0] =
safeAdd(
balances[_assetTokenAddress][0x0],
safeDiv(safeMul(_assetTokenAmount, _ticketAmount), 1 ether)
);
balances[_assetTokenAddress][msg.sender] =
safeSub(
balances[_assetTokenAddress][msg.sender],
safeDiv(safeMul(_assetTokenAmount, _ticketAmount), 1 ether)
);
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
address writerTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
true
);
//issue write option
balances[writerTicketAddress][msg.sender] =
safeAdd(balances[writerTicketAddress][msg.sender], _ticketAmount);
globalBalance[writerTicketAddress] =
safeAdd(globalBalance[writerTicketAddress], _ticketAmount);
//issue hold option
balances[holderTicketAddress][msg.sender] =
safeAdd(balances[holderTicketAddress][msg.sender], _ticketAmount);
globalBalance[holderTicketAddress] =
safeAdd(globalBalance[holderTicketAddress], _ticketAmount);
CreateOption(
msg.sender,
holderTicketAddress,
writerTicketAddress,
_ticketAmount,
(bytes32(_assetTokenAddress) ^ bytes32(_strikeTokenAddress))
);
//check if we need to register, and do if we do.
if (
OptionRegistry(optionsRegistryAddress).isOptionPairRegistered(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires
)
== false
)
{
require(
OptionRegistry(optionsRegistryAddress).registerOptionPair(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires
)
);
}
return _ticketAmount;
}
//if own buy & writer ticket get asset, void tickets
// 1 ticket gets 10^18 option units voided.
function collapseOptionPair( //#66
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires,
uint256 _ticketAmount
)
external
returns (uint256 ticketsCollapsed)
{
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
address writerTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
true
);
//if they have the write option
//if they have the hold option
require (
(balances[holderTicketAddress][msg.sender] >= _ticketAmount)
&& (balances[writerTicketAddress][msg.sender] >= _ticketAmount)
);
//I guess it can be expired, since you have both legs.
//void write option
balances[writerTicketAddress][msg.sender] =
safeSub(balances[writerTicketAddress][msg.sender], _ticketAmount);
globalBalance[writerTicketAddress] =
safeSub(globalBalance[writerTicketAddress], _ticketAmount);
//void hold option
balances[holderTicketAddress][msg.sender] =
safeSub(balances[holderTicketAddress][msg.sender], _ticketAmount);
globalBalance[holderTicketAddress] =
safeSub(globalBalance[holderTicketAddress], _ticketAmount);
//unlock asset
balances[_assetTokenAddress][0x0] = safeSub(
balances[_assetTokenAddress][0x0],
safeDiv(safeMul(_assetTokenAmount, _ticketAmount), 1 ether)
);
balances[_assetTokenAddress][msg.sender] = safeAdd(
balances[_assetTokenAddress][msg.sender],
safeDiv(safeMul(_assetTokenAmount, _ticketAmount), 1 ether)
);
//emit event
CollapseOption(
msg.sender,
holderTicketAddress,
writerTicketAddress,
_ticketAmount,
(bytes32(_assetTokenAddress) ^ bytes32(_strikeTokenAddress))
);
return _ticketAmount;
}
/*about invisableHandOfAdamSmith():
q: why would someone ever want to buy an out-of-the-money,
collaterized call option at strike price?
a: if an american option is executed, and the collateral's movement
makes it later out of the money, the value of the option would
need to be calculated by including the "pre-executed" amount.
*
This would prevent an external actor performing weird arb trades
(write a billion tickets, collapse a billion tickets, profit!).
Skip the middle man! Writers are more likely to get 100% token or
strike at expiry, based on market value, and holders still have
their option intact.
*
Arbers gonna arb. Let them do their thing.
*/
//if there have been executions, Adam Smith can deposit asset, get strike, up to execution amount.
// function invisibleHandOfAdamSmith( //#67
function optionExcerciseUnwind(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires,
uint256 _ticketAmount,
bool _fillOrKill //do we want? probably...
)
external
notLocked
returns (uint256 ticketsUnwound) //(amountTraded)
{
//only before, equal to expiry
require(block.number <= _optionExpires);
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
//if strike-pool[hash] != 0 {
ticketsUnwound = exercisedOptions[holderTicketAddress];
//fill or kill.
require((_fillOrKill == false) || (ticketsUnwound >= _ticketAmount));
//get amount to trade.
if (ticketsUnwound > _ticketAmount) ticketsUnwound = _ticketAmount;
require(ticketsUnwound > 0);
//cant buy zero, either because not avail, or you asked for zero.
//check compliance, like a trade!
require(
(! restrictedTokens[holderTicketAddress]) //if it is not restricted
|| Compliance(complianceAddress).canTrade(holderTicketAddress, msg.sender) // or compliance says yes.
);
//debit balance of caller of asset tokens, credit 0x0
balances[_assetTokenAddress][msg.sender] = safeSub(
balances[_assetTokenAddress][msg.sender],
safeDiv(safeMul(_assetTokenAmount, ticketsUnwound), 1 ether)
);
balances[_assetTokenAddress][0x0] = safeAdd(
balances[_assetTokenAddress][0x0],
safeDiv(safeMul(_assetTokenAmount, ticketsUnwound), 1 ether)
);
//debit balance of exercisedOptions of holdOption, credit caller.
//no change in global balances.
exercisedOptions[holderTicketAddress] =
safeSub(exercisedOptions[holderTicketAddress], ticketsUnwound);
balances[holderTicketAddress][msg.sender] =
safeAdd(balances[holderTicketAddress][msg.sender], ticketsUnwound);
//debit balance of 0x0 of strike, credit caller.
balances[_strikeTokenAddress][0x0] = safeSub(
balances[_strikeTokenAddress][0x0],
safeDiv(safeMul(_strikeTokenAmount, ticketsUnwound), 1 ether)
);
balances[_strikeTokenAddress][msg.sender] = safeAdd(
balances[_strikeTokenAddress][msg.sender],
safeDiv(safeMul(_strikeTokenAmount, ticketsUnwound), 1 ether)
);
//emit event.
ExcerciseUnwind(
msg.sender,
holderTicketAddress,
ticketsUnwound,
(bytes32(_assetTokenAddress) ^ bytes32(_strikeTokenAddress)),
_fillOrKill
);
return ticketsUnwound;
}
//if before expiry, and own hold ticket, then pay strike, get asset, void hold ticket
function excerciseOption( //#68
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires,
uint256 _ticketAmount
)
external
returns (uint256 ticketsExcercised)
{
//only holder before, equal to expiry
require(block.number <= _optionExpires);
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
//get balance of tickets
ticketsExcercised = balances[holderTicketAddress][msg.sender];
require(ticketsExcercised >= _ticketAmount); //its just a balance here.
//get amount to trade.
if (ticketsExcercised > _ticketAmount) ticketsExcercised = _ticketAmount;
//cant execute zero, either you have zero, or you asked for zero.
require(ticketsExcercised > 0);
//debit balance of caller for holdOption, credit exercisedOptions
balances[holderTicketAddress][msg.sender] =
safeSub(balances[holderTicketAddress][msg.sender], ticketsExcercised);
exercisedOptions[holderTicketAddress] =
safeAdd(exercisedOptions[holderTicketAddress], ticketsExcercised);
//debit balance of caller for strikeToken, credit 0x0
balances[_strikeTokenAddress][msg.sender] = safeSub(
balances[_strikeTokenAddress][msg.sender],
safeDiv(safeMul(_strikeTokenAmount, ticketsExcercised), 1 ether)
);
balances[_strikeTokenAddress][0x0] = safeAdd(
balances[_strikeTokenAddress][0x0],
safeDiv(safeMul(_strikeTokenAmount, ticketsExcercised), 1 ether)
);
//debit balance of 0x0 of asset, credit caller.
balances[_assetTokenAddress][0x0] = safeSub(
balances[_assetTokenAddress][0x0],
safeDiv(safeMul(_assetTokenAmount, ticketsExcercised), 1 ether)
);
balances[_assetTokenAddress][msg.sender] = safeAdd(
balances[_assetTokenAddress][msg.sender],
safeDiv(safeMul(_assetTokenAmount, ticketsExcercised), 1 ether)
);
//no change in global balances.
//emit event.
ExcerciseOption(
msg.sender,
holderTicketAddress,
ticketsExcercised,
(bytes32(_assetTokenAddress) ^ bytes32(_strikeTokenAddress))
);
return ticketsExcercised;
}
//if after expiry, get collateral, void option.
function expireOption( //#69
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires,
uint256 _ticketAmount
)
external
returns (uint256 ticketsExpired)
{
//only writer, only after expiry
require(block.number > _optionExpires);
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
address writerTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
true
);
//get balance of tickets
ticketsExpired = balances[writerTicketAddress][msg.sender];
require(ticketsExpired >= _ticketAmount); //its just a balance here.
//get amount to trade.
if (ticketsExpired > _ticketAmount) ticketsExpired = _ticketAmount;
//cant execute zero, either you have zero, or you asked for zero.
require(ticketsExpired > 0);
// debit holder tickets from user, add to exercisedOptions.
balances[writerTicketAddress][msg.sender] =
safeSub(balances[writerTicketAddress][msg.sender], ticketsExpired);
exercisedOptions[writerTicketAddress] =
safeAdd(exercisedOptions[writerTicketAddress], ticketsExpired);
//calculate amounts
uint256 strikeTokenAmount =
safeDiv(
safeMul(
safeDiv(safeMul(ticketsExpired, _strikeTokenAmount), 1 ether), //tickets
exercisedOptions[holderTicketAddress]
),
globalBalance[holderTicketAddress]
);
uint256 assetTokenAmount =
safeDiv(
safeMul(
safeDiv(safeMul(ticketsExpired, _assetTokenAmount), 1 ether), //tickets
safeSub(globalBalance[holderTicketAddress], exercisedOptions[holderTicketAddress])
),
globalBalance[holderTicketAddress]
);
//debit zero, add to msg.sender
balances[_strikeTokenAddress][0x0] =
safeSub(balances[_strikeTokenAddress][0x0], strikeTokenAmount);
balances[_assetTokenAddress][0x0] =
safeSub(balances[_assetTokenAddress][0x0], assetTokenAmount);
balances[_strikeTokenAddress][msg.sender] =
safeAdd(balances[_strikeTokenAddress][msg.sender], strikeTokenAmount);
balances[_assetTokenAddress][msg.sender] =
safeAdd(balances[_assetTokenAddress][msg.sender], assetTokenAmount);
//set inactive
ExpireOption( //#69]
msg.sender,
writerTicketAddress,
ticketsExpired,
(bytes32(_assetTokenAddress) ^ bytes32(_strikeTokenAddress))
);
return ticketsExpired;
}
//get an option's Hash's address
// (•_•) ( •_•)>⌐■-■ (⌐■_■)
//
//going from 32 bytes to 20 bytes still gives us 160 bits of hash goodness.
//that's still a crazy large number, and used by ethereum for addresses.
function getOptionAddress(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires,
bool _isWriter
)
public
view
returns(address)
{
return(
address(
keccak256(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
_isWriter
)
)
);
}
/***********************************
/ Options View Code
************************************/
//since the options code might move, we should have a way to always
//call a function to this contract to get the current values
function testIsOptionPairRegistered(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
)
external
view
returns(bool)
{
return(
OptionRegistry(optionsRegistryAddress).isOptionPairRegistered(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires
)
);
}
/***********************************
/ Default Options Registration Code
************************************/
// Register emits an event and adds it to restrictedToken.
// We'll deal with any other needed registration later.
// Set up for upgradeable external contract.
// return bools.
event RegisterOptionsPair(
bytes32 indexed optionPair, //assetTokenAddress xor strikeTokenAddress
address indexed writerTicketAddress,
address indexed holderTicketAddress,
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
);
function registerOptionPair(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
)
public
returns(bool)
{
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
// if (
// isOptionPairRegistered(
// _assetTokenAddress,
// _assetTokenAmount,
// _strikeTokenAddress,
// _strikeTokenAmount,
// _optionExpires
// )
// )
//cheaper not to make call gaswise, same result.
if (restrictedTokens[holderTicketAddress]) {
return false;
//return halts execution, but else is better for readibility
} else {
address writerTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
true
);
restrictedTokens[holderTicketAddress] = true;
restrictedTokens[writerTicketAddress] = true;
//an external contract would need to call something like this:
// after being registered as a helper contract on the main site.
//UberDelta(uberdeltaAddress).tokenRestriction(holderTicketAddress, true);
//UberDelta(uberdeltaAddress).tokenRestriction(writerTicketAddress, true);
RegisterOptionsPair(
(bytes32(_assetTokenAddress) ^ bytes32(_strikeTokenAddress)),
holderTicketAddress,
writerTicketAddress,
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires
);
return(true);
}
}
// for v1, we'll simply return if there's a restriction.
function isOptionPairRegistered(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
)
public
view
returns(bool)
{
address holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
return(restrictedTokens[holderTicketAddress]);
}
function getOptionPair(
address _assetTokenAddress,
uint256 _assetTokenAmount,
address _strikeTokenAddress,
uint256 _strikeTokenAmount,
uint256 _optionExpires
)
public
view
returns(address holderTicketAddress, address writerTicketAddress)
{
holderTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
false
);
writerTicketAddress = getOptionAddress(
_assetTokenAddress,
_assetTokenAmount,
_strikeTokenAddress,
_strikeTokenAmount,
_optionExpires,
true
);
return(holderTicketAddress, writerTicketAddress);
}
/******************
/ EOS Registration
******************/
// some users will accidentally keep EOS on the exchange during the snapshot.
function EOSRegistration (string _key) external onlyOwner{
EOS(0xd0a6E6C54DbC68Db5db3A091B171A77407Ff7ccf).register(_key);
}
}
{
"compilationTarget": {
"UberDelta.sol": "UberDelta"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 999
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_getUser","type":"address"},{"name":"_giveUser","type":"address"}],"name":"validateTrade","outputs":[{"name":"isAllowed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newExchange","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"getUserFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"upgrade","outputs":[{"name":"moveBalance","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_user","type":"address"},{"name":"_newClass","type":"uint256"}],"name":"changeUserClass","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"},{"name":"_isWriter","type":"bool"}],"name":"getOptionAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"userClass","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"name":"availableVolume","outputs":[{"name":"amountRemaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_toUser","type":"address"},{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"depositTokenToUser","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_withdrawAmount","type":"uint256"},{"name":"_feeAmount","type":"uint256"},{"name":"_withdrawExpires","type":"uint256"},{"name":"_salt","type":"uint256"},{"name":"_maker","type":"address"},{"name":"_restrictedTo","type":"address"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"name":"remoteWithdraw","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_class","type":"uint256"},{"name":"_fee","type":"uint256"}],"name":"changeClassFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_user","type":"address"}],"name":"testCanTrade","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_withdrawAmount","type":"uint256"},{"name":"_feeAmount","type":"uint256"},{"name":"_withdrawExpires","type":"uint256"},{"name":"_salt","type":"uint256"},{"name":"_maker","type":"address"},{"name":"_restrictedTo","type":"address"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"},{"name":"_sender","type":"address"}],"name":"testRemoteWithdraw","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"contractLocked","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"depositToken","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"}],"name":"isOptionPairRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"canDeposit","outputs":[{"name":"isAllowed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_key","type":"string"}],"name":"EOSRegistration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"feeByClass","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newComplianceAddress","type":"address"}],"name":"updateComplianceAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_toUser","type":"address"},{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"internalTransfer","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newExchange","type":"address"}],"name":"changeNewExchange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"},{"name":"_amount","type":"uint256"},{"name":"_fillOrKill","type":"bool"}],"name":"trade","outputs":[{"name":"tradeAmount","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_user","type":"address"}],"name":"canTrade","outputs":[{"name":"isAllowed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeAccount","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"exercisedOptions","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_feeAccount","type":"address"}],"name":"changeFeeAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"optionsRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"},{"name":"_ticketAmount","type":"uint256"},{"name":"_fillOrKill","type":"bool"}],"name":"optionExcerciseUnwind","outputs":[{"name":"ticketsUnwound","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"}],"name":"order","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"}],"name":"testIsOptionPairRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_lock","type":"bool"}],"name":"lockContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_getUser","type":"address"},{"name":"_giveUser","type":"address"}],"name":"testValidateTrade","outputs":[{"name":"isAllowed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newManager","type":"address"}],"name":"newManager","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"globalBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"orders","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawToken","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"}],"name":"getOptionPair","outputs":[{"name":"holderTicketAddress","type":"address"},{"name":"writerTicketAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_toUser","type":"address"}],"name":"depositToUser","outputs":[{"name":"success","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_newToken","type":"address"},{"name":"_status","type":"bool"}],"name":"tokenRestriction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newOptionsRegistryAddress","type":"address"}],"name":"updateOptionsRegistryAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isHelper","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_value","type":"uint256"},{"name":"_sendTo","type":"bytes"}],"name":"tokenFallback","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"sweepAccount","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"},{"name":"_ticketAmount","type":"uint256"}],"name":"collapseOptionPair","outputs":[{"name":"ticketsCollapsed","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"}],"name":"amountFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"},{"name":"_ticketAmount","type":"uint256"}],"name":"expireOption","outputs":[{"name":"ticketsExpired","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"},{"name":"_amount","type":"uint256"},{"name":"_sender","type":"address"},{"name":"_fillOrKill","type":"bool"}],"name":"testTrade","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"confirmOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_helper","type":"address"},{"name":"_status","type":"bool"}],"name":"changeHelper","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"restrictedTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_withdrawAmount","type":"uint256"},{"name":"_feeAmount","type":"uint256"},{"name":"_withdrawExpires","type":"uint256"},{"name":"_salt","type":"uint256"},{"name":"_restrictedTo","type":"address"}],"name":"cancelRemoteWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"sweepTokenAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"}],"name":"registerOptionPair","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"}],"name":"testCanDeposit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"},{"name":"_ticketAmount","type":"uint256"}],"name":"createOptionPair","outputs":[{"name":"ticketsCreated","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"}],"name":"getHash","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_addressData","type":"address[4]"},{"name":"_numberData","type":"uint256[4]"}],"name":"cancelOrder","outputs":[{"name":"amountCancelled","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"orderFills","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_sweepAccount","type":"address"}],"name":"changeSweepAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"complianceAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_assetTokenAddress","type":"address"},{"name":"_assetTokenAmount","type":"uint256"},{"name":"_strikeTokenAddress","type":"address"},{"name":"_strikeTokenAmount","type":"uint256"},{"name":"_optionExpires","type":"uint256"},{"name":"_ticketAmount","type":"uint256"}],"name":"excerciseOption","outputs":[{"name":"ticketsExcercised","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tradePair","type":"bytes32"},{"indexed":true,"name":"maker","type":"address"},{"indexed":false,"name":"addressData","type":"address[4]"},{"indexed":false,"name":"numberData","type":"uint256[4]"}],"name":"Order","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tradePair","type":"bytes32"},{"indexed":true,"name":"maker","type":"address"},{"indexed":false,"name":"addressData","type":"address[4]"},{"indexed":false,"name":"numberData","type":"uint256[4]"},{"indexed":false,"name":"status","type":"uint256"}],"name":"Cancel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tradePair","type":"bytes32"},{"indexed":true,"name":"taker","type":"address"},{"indexed":false,"name":"hash","type":"bytes32"},{"indexed":false,"name":"failReason","type":"uint256"}],"name":"FailedTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tradePair","type":"bytes32"},{"indexed":true,"name":"maker","type":"address"},{"indexed":true,"name":"taker","type":"address"},{"indexed":false,"name":"makerToken","type":"address"},{"indexed":false,"name":"takerToken","type":"address"},{"indexed":false,"name":"restrictedTo","type":"address"},{"indexed":false,"name":"numberData","type":"uint256[4]"},{"indexed":false,"name":"tradeAmount","type":"uint256"},{"indexed":false,"name":"fillOrKill","type":"bool"}],"name":"Trade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"toUser","type":"address"},{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"toUser","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"toUser","type":"address"},{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"InternalTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":true,"name":"sweeper","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"balance","type":"uint256"}],"name":"TokenSweep","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"status","type":"bool"}],"name":"RestrictToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newExchange","type":"address"}],"name":"NewExchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeAccount","type":"address"}],"name":"ChangeFeeAccount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sweepAccount","type":"address"}],"name":"ChangeSweepAccount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"class","type":"uint256"},{"indexed":false,"name":"fee","type":"uint256"}],"name":"ChangeClassFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"class","type":"uint256"}],"name":"ChangeUserClass","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"status","type":"bool"}],"name":"LockContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newComplianceAddress","type":"address"}],"name":"UpdateComplianceAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOptionsRegistryAddress","type":"address"}],"name":"UpdateOptionsRegistryAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"newExchange","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Upgrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"maker","type":"address"},{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"withdrawAmount","type":"uint256"},{"indexed":false,"name":"feeAmount","type":"uint256"},{"indexed":false,"name":"withdrawExpires","type":"uint256"},{"indexed":false,"name":"salt","type":"uint256"},{"indexed":false,"name":"restrictedTo","type":"address"}],"name":"RemoteWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"maker","type":"address"},{"indexed":false,"name":"withdrawAmount","type":"uint256"},{"indexed":false,"name":"feeAmount","type":"uint256"},{"indexed":false,"name":"withdrawExpires","type":"uint256"},{"indexed":false,"name":"salt","type":"uint256"},{"indexed":false,"name":"restrictedTo","type":"address"},{"indexed":false,"name":"status","type":"uint256"}],"name":"CancelRemoteWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"holderTicketAddress","type":"address"},{"indexed":true,"name":"writerTicketAddress","type":"address"},{"indexed":false,"name":"ticketsCollapsed","type":"uint256"},{"indexed":false,"name":"optionPair","type":"bytes32"}],"name":"CollapseOption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"holderTicketAddress","type":"address"},{"indexed":false,"name":"ticketsUnwound","type":"uint256"},{"indexed":false,"name":"optionPair","type":"bytes32"},{"indexed":false,"name":"fillOrKill","type":"bool"}],"name":"ExcerciseUnwind","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"writerTicketAddress","type":"address"},{"indexed":false,"name":"ticketsExpired","type":"uint256"},{"indexed":false,"name":"optionPair","type":"bytes32"}],"name":"ExpireOption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"holderTicketAddress","type":"address"},{"indexed":true,"name":"writerTicketAddress","type":"address"},{"indexed":false,"name":"ticketsCreated","type":"uint256"},{"indexed":false,"name":"optionPair","type":"bytes32"}],"name":"CreateOption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"holderTicketAddress","type":"address"},{"indexed":false,"name":"ticketsExcercised","type":"uint256"},{"indexed":false,"name":"optionPair","type":"bytes32"}],"name":"ExcerciseOption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"optionPair","type":"bytes32"},{"indexed":true,"name":"writerTicketAddress","type":"address"},{"indexed":true,"name":"holderTicketAddress","type":"address"},{"indexed":false,"name":"_assetTokenAddress","type":"address"},{"indexed":false,"name":"_assetTokenAmount","type":"uint256"},{"indexed":false,"name":"_strikeTokenAddress","type":"address"},{"indexed":false,"name":"_strikeTokenAmount","type":"uint256"},{"indexed":false,"name":"_optionExpires","type":"uint256"}],"name":"RegisterOptionsPair","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"helper","type":"address"},{"indexed":false,"name":"status","type":"bool"}],"name":"ChangeHelper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_newManager","type":"address"}],"name":"NewManager","type":"event"}]