Transaction Hash:
Block:
10387934 at Jul-03-2020 05:59:06 PM +UTC
Transaction Fee:
0.006411668 ETH
$13.29
Gas Used:
206,828 Gas / 31 Gwei
Emitted Events:
| 175 |
Dai.Transfer( src=[Sender] 0x7bf1918067027d112624c1a092fec3096b8357ca, dst=[Receiver] 0x34618d5714860262dbf6fdad0598fca7fe47d244, wad=15000000000000000000 )
|
| 176 |
Dai.Transfer( src=[Receiver] 0x34618d5714860262dbf6fdad0598fca7fe47d244, dst=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, wad=15000000000000000000 )
|
| 177 |
Dai.Approval( src=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, guy=ConditionalTokens, wad=14700000000000000000 )
|
| 178 |
Dai.Transfer( src=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, dst=ConditionalTokens, wad=14700000000000000000 )
|
| 179 |
ConditionalTokens.TransferBatch( operator=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, from=0x00000000...000000000, to=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, ids=[30724654842089398204840126816086110978630898353379970100274555029951945477487, 74631146513732088214369488480009490810396011984268855962650074513379711097913], values=[14700000000000000000, 14700000000000000000] )
|
| 180 |
ConditionalTokens.PositionSplit( stakeholder=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, collateralToken=Dai, parentCollectionId=0000000000000000000000000000000000000000000000000000000000000000, conditionId=A49E9AFE700AE90820E484BB6D5FB5C37B66461F66473B34566E8F02F50E9F5E, partition=[1, 2], amount=14700000000000000000 )
|
| 181 |
ConditionalTokens.TransferSingle( operator=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, from=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, to=[Receiver] 0x34618d5714860262dbf6fdad0598fca7fe47d244, id=30724654842089398204840126816086110978630898353379970100274555029951945477487, value=46978053496465569890 )
|
| 182 |
0xb98ebfda7ce0f5f2c13b1af319992137d697ece7.0x4f62630f51608fc8a7603a9391a5101e58bd7c276139366fc107dc3b67c3dcf8( 0x4f62630f51608fc8a7603a9391a5101e58bd7c276139366fc107dc3b67c3dcf8, 0x00000000000000000000000034618d5714860262dbf6fdad0598fca7fe47d244, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000d02ab486cedc0000, 0000000000000000000000000000000000000000000000000429d069189e0000, 0000000000000000000000000000000000000000000000028bf392c13020b862 )
|
| 183 |
0x34618d5714860262dbf6fdad0598fca7fe47d244.0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e( 0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e, b19eb18fbcfa7c77a66572cb24a7376c838c548626e6dadfcde1d693c5c7823c, 0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x005e288D...8688C7223
Miner
| (xnpool) | 240.920348197386321358 Eth | 240.926759865386321358 Eth | 0.006411668 | |
| 0x34618D57...7fe47d244 | |||||
| 0x6B175474...495271d0F | |||||
| 0x7bf19180...96b8357cA |
0.0252714082 Eth
Nonce: 6
|
0.0188597402 Eth
Nonce: 7
| 0.006411668 | ||
| 0xb98EBfDa...7d697ecE7 | |||||
| 0xC59b0e4D...192407E0C |
Execution Trace
0x34618d5714860262dbf6fdad0598fca7fe47d244.6a761202( )
GnosisSafe.execTransaction( to=0xB522a9f781924eD250A11C54105E51840B138AdD, value=0, data=0x8D80FF0A00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000172006B175474E89094C44DA98B954EEDEAC495271D0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423B872DD0000000000000000000000007BF1918067027D112624C1A092FEC3096B8357CA00000000000000000000000034618D5714860262DBF6FDAD0598FCA7FE47D244000000000000000000000000000000000000000000000000D02AB486CEDC000000B98EBFDA7CE0F5F2C13B1AF319992137D697ECE70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006440993B26000000000000000000000000000000000000000000000000D02AB486CEDC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028BF392C13020B8620000000000000000000000000000, operation=1, safeTxGas=0, baseGas=0, gasPrice=0, gasToken=0x0000000000000000000000000000000000000000, refundReceiver=0x0000000000000000000000000000000000000000, signatures=0x0000000000000000000000007BF1918067027D112624C1A092FEC3096B8357CA000000000000000000000000000000000000000000000000000000000000000001 ) => ( success=True )MultiSend.multiSend( transactions=0x006B175474E89094C44DA98B954EEDEAC495271D0F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006423B872DD0000000000000000000000007BF1918067027D112624C1A092FEC3096B8357CA00000000000000000000000034618D5714860262DBF6FDAD0598FCA7FE47D244000000000000000000000000000000000000000000000000D02AB486CEDC000000B98EBFDA7CE0F5F2C13B1AF319992137D697ECE70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006440993B26000000000000000000000000000000000000000000000000D02AB486CEDC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028BF392C13020B862 )-
Dai.transferFrom( src=0x7bf1918067027d112624C1a092FEC3096b8357cA, dst=0x34618D5714860262dbf6fDAD0598fca7fe47d244, wad=15000000000000000000 ) => ( True )
0xb98ebfda7ce0f5f2c13b1af319992137d697ece7.40993b26( )
FixedProductMarketMaker.buy( investmentAmount=15000000000000000000, outcomeIndex=0, minOutcomeTokensToBuy=46978053496465569890 )-
ConditionalTokens.balanceOfBatch( owners=[0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, 0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7], ids=[30724654842089398204840126816086110978630898353379970100274555029951945477487, 74631146513732088214369488480009490810396011984268855962650074513379711097913] ) => ( [43607554623089660250, 5159656438998422670] )
-
Dai.transferFrom( src=0x34618D5714860262dbf6fDAD0598fca7fe47d244, dst=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, wad=15000000000000000000 ) => ( True )
-
Dai.approve( usr=0xC59b0e4De5F1248C1140964E0fF287B192407E0C, wad=14700000000000000000 ) => ( True )
ConditionalTokens.splitPosition( collateralToken=0x6B175474E89094C44Da98b954EedeAC495271d0F, parentCollectionId=0000000000000000000000000000000000000000000000000000000000000000, conditionId=A49E9AFE700AE90820E484BB6D5FB5C37B66461F66473B34566E8F02F50E9F5E, partition=[1, 2], amount=14700000000000000000 )
-
Dai.transferFrom( src=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, dst=0xC59b0e4De5F1248C1140964E0fF287B192407E0C, wad=14700000000000000000 ) => ( True )
-
0xb98ebfda7ce0f5f2c13b1af319992137d697ece7.bc197c81( )
-
ConditionalTokens.safeTransferFrom( from=0xb98EBfDa7ce0F5F2C13b1Af319992137d697ecE7, to=0x34618D5714860262dbf6fDAD0598fca7fe47d244, id=30724654842089398204840126816086110978630898353379970100274555029951945477487, value=46978053496465569890, data=0x )
-
0x34618d5714860262dbf6fdad0598fca7fe47d244.f23a6e61( )
-
-
-
File 1 of 5: Dai
File 2 of 5: ConditionalTokens
File 3 of 5: GnosisSafe
File 4 of 5: MultiSend
File 5 of 5: FixedProductMarketMaker
// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
pragma solidity =0.5.12;
////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity 0.5.12; */
contract LibNote {
event LogNote(
bytes4 indexed sig,
address indexed usr,
bytes32 indexed arg1,
bytes32 indexed arg2,
bytes data
) anonymous;
modifier note {
_;
assembly {
// log an 'anonymous' event with a constant 6 words of calldata
// and four indexed topics: selector, caller, arg1 and arg2
let mark := msize // end of memory ensures zero
mstore(0x40, add(mark, 288)) // update free memory pointer
mstore(mark, 0x20) // bytes type data offset
mstore(add(mark, 0x20), 224) // bytes size (padded)
calldatacopy(add(mark, 0x40), 0, 224) // bytes payload
log4(mark, 288, // calldata
shl(224, shr(224, calldataload(0))), // msg.sig
caller, // msg.sender
calldataload(4), // arg1
calldataload(36) // arg2
)
}
}
}
////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
// Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/* pragma solidity 0.5.12; */
/* import "./lib.sol"; */
contract Dai is LibNote {
// --- Auth ---
mapping (address => uint) public wards;
function rely(address guy) external note auth { wards[guy] = 1; }
function deny(address guy) external note auth { wards[guy] = 0; }
modifier auth {
require(wards[msg.sender] == 1, "Dai/not-authorized");
_;
}
// --- ERC20 Data ---
string public constant name = "Dai Stablecoin";
string public constant symbol = "DAI";
string public constant version = "1";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping (address => uint) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;
mapping (address => uint) public nonces;
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
// --- Math ---
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
// --- EIP712 niceties ---
bytes32 public DOMAIN_SEPARATOR;
// bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
constructor(uint256 chainId_) public {
wards[msg.sender] = 1;
DOMAIN_SEPARATOR = keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes(version)),
chainId_,
address(this)
));
}
// --- Token ---
function transfer(address dst, uint wad) external returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad)
public returns (bool)
{
require(balanceOf[src] >= wad, "Dai/insufficient-balance");
if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
}
balanceOf[src] = sub(balanceOf[src], wad);
balanceOf[dst] = add(balanceOf[dst], wad);
emit Transfer(src, dst, wad);
return true;
}
function mint(address usr, uint wad) external auth {
balanceOf[usr] = add(balanceOf[usr], wad);
totalSupply = add(totalSupply, wad);
emit Transfer(address(0), usr, wad);
}
function burn(address usr, uint wad) external {
require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
}
balanceOf[usr] = sub(balanceOf[usr], wad);
totalSupply = sub(totalSupply, wad);
emit Transfer(usr, address(0), wad);
}
function approve(address usr, uint wad) external returns (bool) {
allowance[msg.sender][usr] = wad;
emit Approval(msg.sender, usr, wad);
return true;
}
// --- Alias ---
function push(address usr, uint wad) external {
transferFrom(msg.sender, usr, wad);
}
function pull(address usr, uint wad) external {
transferFrom(usr, msg.sender, wad);
}
function move(address src, address dst, uint wad) external {
transferFrom(src, dst, wad);
}
// --- Approve by signature ---
function permit(address holder, address spender, uint256 nonce, uint256 expiry,
bool allowed, uint8 v, bytes32 r, bytes32 s) external
{
bytes32 digest =
keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH,
holder,
spender,
nonce,
expiry,
allowed))
));
require(holder != address(0), "Dai/invalid-address-0");
require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
require(expiry == 0 || now <= expiry, "Dai/permit-expired");
require(nonce == nonces[holder]++, "Dai/invalid-nonce");
uint wad = allowed ? uint(-1) : 0;
allowance[holder][spender] = wad;
emit Approval(holder, spender, wad);
}
}File 2 of 5: ConditionalTokens
// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: openzeppelin-solidity/contracts/introspection/IERC165.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-165).
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others (`ERC165Checker`).
*
* For an implementation, see `ERC165`.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: contracts/ERC1155/IERC1155.sol
pragma solidity ^0.5.0;
/**
@title ERC-1155 Multi Token Standard basic interface
@dev See https://eips.ethereum.org/EIPS/eip-1155
*/
contract IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address owner, uint256 id) public view returns (uint256);
function balanceOfBatch(address[] memory owners, uint256[] memory ids) public view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external;
}
// File: contracts/ERC1155/IERC1155TokenReceiver.sol
pragma solidity ^0.5.0;
/**
@title ERC-1155 Multi Token Receiver Interface
@dev See https://eips.ethereum.org/EIPS/eip-1155
*/
contract IERC1155TokenReceiver is IERC165 {
/**
@dev Handles the receipt of a single ERC1155 token type. This function is
called at the end of a `safeTransferFrom` after the balance has been updated.
To accept the transfer, this must return
`bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
(i.e. 0xf23a6e61, or its own function selector).
@param operator The address which initiated the transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param id The ID of the token being transferred
@param value The amount of tokens being transferred
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
)
external
returns(bytes4);
/**
@dev Handles the receipt of a multiple ERC1155 token types. This function
is called at the end of a `safeBatchTransferFrom` after the balances have
been updated. To accept the transfer(s), this must return
`bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
(i.e. 0xbc197c81, or its own function selector).
@param operator The address which initiated the batch transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param ids An array containing ids of each token being transferred (order and length must match values array)
@param values An array containing amounts of each token being transferred (order and length must match ids array)
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
returns(bytes4);
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: openzeppelin-solidity/contracts/utils/Address.sol
pragma solidity ^0.5.0;
/**
* @dev Collection of functions related to the address type,
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
// File: openzeppelin-solidity/contracts/introspection/ERC165.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the `IERC165` interface.
*
* Contracts may inherit from this and call `_registerInterface` to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See `IERC165.supportsInterface`.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See `IERC165.supportsInterface`.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
// File: contracts/ERC1155/ERC1155.sol
pragma solidity ^0.5.0;
/**
* @title Standard ERC1155 token
*
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*/
contract ERC1155 is ERC165, IERC1155
{
using SafeMath for uint256;
using Address for address;
// Mapping from token ID to owner balances
mapping (uint256 => mapping(address => uint256)) private _balances;
// Mapping from owner to operator approvals
mapping (address => mapping(address => bool)) private _operatorApprovals;
constructor()
public
{
_registerInterface(
ERC1155(0).safeTransferFrom.selector ^
ERC1155(0).safeBatchTransferFrom.selector ^
ERC1155(0).balanceOf.selector ^
ERC1155(0).balanceOfBatch.selector ^
ERC1155(0).setApprovalForAll.selector ^
ERC1155(0).isApprovedForAll.selector
);
}
/**
@dev Get the specified address' balance for token with specified ID.
@param owner The address of the token holder
@param id ID of the token
@return The owner's balance of the token type requested
*/
function balanceOf(address owner, uint256 id) public view returns (uint256) {
require(owner != address(0), "ERC1155: balance query for the zero address");
return _balances[id][owner];
}
/**
@dev Get the balance of multiple account/token pairs
@param owners The addresses of the token holders
@param ids IDs of the tokens
@return Balances for each owner and token id pair
*/
function balanceOfBatch(
address[] memory owners,
uint256[] memory ids
)
public
view
returns (uint256[] memory)
{
require(owners.length == ids.length, "ERC1155: owners and IDs must have same lengths");
uint256[] memory batchBalances = new uint256[](owners.length);
for (uint256 i = 0; i < owners.length; ++i) {
require(owners[i] != address(0), "ERC1155: some address in batch balance query is zero");
batchBalances[i] = _balances[ids[i]][owners[i]];
}
return batchBalances;
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address operator, bool approved) external {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
/**
@notice Queries the approval status of an operator for a given owner.
@param owner The owner of the Tokens
@param operator Address of authorized operator
@return True if the operator is approved, false if not
*/
function isApprovedForAll(address owner, address operator) external view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
@dev Transfers `value` amount of an `id` from the `from` address to the `to` address specified.
Caller must be approved to manage the tokens being transferred out of the `from` account.
If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately.
@param from Source address
@param to Target address
@param id ID of the token type
@param value Transfer amount
@param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 value,
bytes calldata data
)
external
{
require(to != address(0), "ERC1155: target address must be non-zero");
require(
from == msg.sender || _operatorApprovals[from][msg.sender] == true,
"ERC1155: need operator approval for 3rd party transfers."
);
_balances[id][from] = _balances[id][from].sub(value);
_balances[id][to] = value.add(_balances[id][to]);
emit TransferSingle(msg.sender, from, to, id, value);
_doSafeTransferAcceptanceCheck(msg.sender, from, to, id, value, data);
}
/**
@dev Transfers `values` amount(s) of `ids` from the `from` address to the
`to` address specified. Caller must be approved to manage the tokens being
transferred out of the `from` account. If `to` is a smart contract, will
call `onERC1155BatchReceived` on `to` and act appropriately.
@param from Source address
@param to Target address
@param ids IDs of each token type
@param values Transfer amounts per token type
@param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
{
require(ids.length == values.length, "ERC1155: IDs and values must have same lengths");
require(to != address(0), "ERC1155: target address must be non-zero");
require(
from == msg.sender || _operatorApprovals[from][msg.sender] == true,
"ERC1155: need operator approval for 3rd party transfers."
);
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 value = values[i];
_balances[id][from] = _balances[id][from].sub(value);
_balances[id][to] = value.add(_balances[id][to]);
}
emit TransferBatch(msg.sender, from, to, ids, values);
_doSafeBatchTransferAcceptanceCheck(msg.sender, from, to, ids, values, data);
}
/**
* @dev Internal function to mint an amount of a token with the given ID
* @param to The address that will own the minted token
* @param id ID of the token to be minted
* @param value Amount of the token to be minted
* @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
require(to != address(0), "ERC1155: mint to the zero address");
_balances[id][to] = value.add(_balances[id][to]);
emit TransferSingle(msg.sender, address(0), to, id, value);
_doSafeTransferAcceptanceCheck(msg.sender, address(0), to, id, value, data);
}
/**
* @dev Internal function to batch mint amounts of tokens with the given IDs
* @param to The address that will own the minted token
* @param ids IDs of the tokens to be minted
* @param values Amounts of the tokens to be minted
* @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function _batchMint(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
require(to != address(0), "ERC1155: batch mint to the zero address");
require(ids.length == values.length, "ERC1155: IDs and values must have same lengths");
for(uint i = 0; i < ids.length; i++) {
_balances[ids[i]][to] = values[i].add(_balances[ids[i]][to]);
}
emit TransferBatch(msg.sender, address(0), to, ids, values);
_doSafeBatchTransferAcceptanceCheck(msg.sender, address(0), to, ids, values, data);
}
/**
* @dev Internal function to burn an amount of a token with the given ID
* @param owner Account which owns the token to be burnt
* @param id ID of the token to be burnt
* @param value Amount of the token to be burnt
*/
function _burn(address owner, uint256 id, uint256 value) internal {
_balances[id][owner] = _balances[id][owner].sub(value);
emit TransferSingle(msg.sender, owner, address(0), id, value);
}
/**
* @dev Internal function to batch burn an amounts of tokens with the given IDs
* @param owner Account which owns the token to be burnt
* @param ids IDs of the tokens to be burnt
* @param values Amounts of the tokens to be burnt
*/
function _batchBurn(address owner, uint256[] memory ids, uint256[] memory values) internal {
require(ids.length == values.length, "ERC1155: IDs and values must have same lengths");
for(uint i = 0; i < ids.length; i++) {
_balances[ids[i]][owner] = _balances[ids[i]][owner].sub(values[i]);
}
emit TransferBatch(msg.sender, owner, address(0), ids, values);
}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
)
internal
{
if(to.isContract()) {
require(
IERC1155TokenReceiver(to).onERC1155Received(operator, from, id, value, data) ==
IERC1155TokenReceiver(to).onERC1155Received.selector,
"ERC1155: got unknown value from onERC1155Received"
);
}
}
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
)
internal
{
if(to.isContract()) {
require(
IERC1155TokenReceiver(to).onERC1155BatchReceived(operator, from, ids, values, data) == IERC1155TokenReceiver(to).onERC1155BatchReceived.selector,
"ERC1155: got unknown value from onERC1155BatchReceived"
);
}
}
}
// File: contracts/CTHelpers.sol
pragma solidity ^0.5.1;
library CTHelpers {
/// @dev Constructs a condition ID from an oracle, a question ID, and the outcome slot count for the question.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
function getConditionId(address oracle, bytes32 questionId, uint outcomeSlotCount) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount));
}
uint constant P = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
uint constant B = 3;
function sqrt(uint x) private pure returns (uint y) {
uint p = P;
// solium-disable-next-line security/no-inline-assembly
assembly {
// add chain generated via https://crypto.stackexchange.com/q/27179/71252
// and transformed to the following program:
// x=1; y=x+x; z=y+y; z=z+z; y=y+z; x=x+y; y=y+x; z=y+y; t=z+z; t=z+t; t=t+t;
// t=t+t; z=z+t; x=x+z; z=x+x; z=z+z; y=y+z; z=y+y; z=z+z; z=z+z; z=y+z; x=x+z;
// z=x+x; z=z+z; z=z+z; z=x+z; y=y+z; x=x+y; z=x+x; z=z+z; y=y+z; z=y+y; t=z+z;
// t=t+t; t=t+t; z=z+t; x=x+z; y=y+x; z=y+y; z=z+z; z=z+z; x=x+z; z=x+x; z=z+z;
// z=x+z; z=z+z; z=z+z; z=x+z; y=y+z; z=y+y; t=z+z; t=t+t; t=z+t; t=y+t; t=t+t;
// t=t+t; t=t+t; t=t+t; z=z+t; x=x+z; z=x+x; z=x+z; y=y+z; z=y+y; z=y+z; z=z+z;
// t=z+z; t=z+t; w=t+t; w=w+w; w=w+w; w=w+w; w=w+w; t=t+w; z=z+t; x=x+z; y=y+x;
// z=y+y; x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; z=x+x; z=x+z; z=z+z; y=y+z; z=y+y;
// z=z+z; x=x+z; y=y+x; z=y+y; z=y+z; x=x+z; y=y+x; x=x+y; y=y+x; z=y+y; z=z+z;
// z=y+z; x=x+z; z=x+x; z=x+z; y=y+z; x=x+y; y=y+x; x=x+y; y=y+x; z=y+y; z=y+z;
// z=z+z; x=x+z; y=y+x; z=y+y; z=y+z; z=z+z; x=x+z; z=x+x; t=z+z; t=t+t; t=z+t;
// t=x+t; t=t+t; t=t+t; t=t+t; t=t+t; z=z+t; y=y+z; x=x+y; y=y+x; x=x+y; z=x+x;
// z=x+z; z=z+z; z=z+z; z=z+z; z=x+z; y=y+z; z=y+y; z=y+z; z=z+z; x=x+z; z=x+x;
// z=x+z; y=y+z; x=x+y; z=x+x; z=z+z; y=y+z; x=x+y; z=x+x; y=y+z; x=x+y; y=y+x;
// z=y+y; z=y+z; x=x+z; y=y+x; z=y+y; z=y+z; z=z+z; z=z+z; x=x+z; z=x+x; z=z+z;
// z=z+z; z=x+z; y=y+z; x=x+y; z=x+x; t=x+z; t=t+t; t=t+t; z=z+t; y=y+z; z=y+y;
// x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; y=y+x; z=y+y; t=y+z; z=y+t; z=z+z; z=z+z;
// z=t+z; x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; z=x+x; z=x+z; y=y+z; x=x+y; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; res=y+x
// res == (P + 1) // 4
y := mulmod(x, x, p)
{
let z := mulmod(y, y, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
{
let t := mulmod(z, z, p)
t := mulmod(z, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
t := mulmod(z, z, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
t := mulmod(z, z, p)
t := mulmod(t, t, p)
t := mulmod(z, t, p)
t := mulmod(y, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
t := mulmod(z, z, p)
t := mulmod(z, t, p)
{
let w := mulmod(t, t, p)
w := mulmod(w, w, p)
w := mulmod(w, w, p)
w := mulmod(w, w, p)
w := mulmod(w, w, p)
t := mulmod(t, w, p)
}
z := mulmod(z, t, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
t := mulmod(z, z, p)
t := mulmod(t, t, p)
t := mulmod(z, t, p)
t := mulmod(x, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
t := mulmod(x, z, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
t := mulmod(y, z, p)
z := mulmod(y, t, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(t, z, p)
}
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
}
x := mulmod(x, y, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
y := mulmod(y, x, p)
}
}
/// @dev Constructs an outcome collection ID from a parent collection and an outcome collection.
/// @param parentCollectionId Collection ID of the parent outcome collection, or bytes32(0) if there's no parent.
/// @param conditionId Condition ID of the outcome collection to combine with the parent outcome collection.
/// @param indexSet Index set of the outcome collection to combine with the parent outcome collection.
function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint indexSet) internal view returns (bytes32) {
uint x1 = uint(keccak256(abi.encodePacked(conditionId, indexSet)));
bool odd = x1 >> 255 != 0;
uint y1;
uint yy;
do {
x1 = addmod(x1, 1, P);
yy = addmod(mulmod(x1, mulmod(x1, x1, P), P), B, P);
y1 = sqrt(yy);
} while(mulmod(y1, y1, P) != yy);
if(odd && y1 % 2 == 0 || !odd && y1 % 2 == 1)
y1 = P - y1;
uint x2 = uint(parentCollectionId);
if(x2 != 0) {
odd = x2 >> 254 != 0;
x2 = (x2 << 2) >> 2;
yy = addmod(mulmod(x2, mulmod(x2, x2, P), P), B, P);
uint y2 = sqrt(yy);
if(odd && y2 % 2 == 0 || !odd && y2 % 2 == 1)
y2 = P - y2;
require(mulmod(y2, y2, P) == yy, "invalid parent collection ID");
(bool success, bytes memory ret) = address(6).staticcall(abi.encode(x1, y1, x2, y2));
require(success, "ecadd failed");
(x1, y1) = abi.decode(ret, (uint, uint));
}
if(y1 % 2 == 1)
x1 ^= 1 << 254;
return bytes32(x1);
}
/// @dev Constructs a position ID from a collateral token and an outcome collection. These IDs are used as the ERC-1155 ID for this contract.
/// @param collateralToken Collateral token which backs the position.
/// @param collectionId ID of the outcome collection associated with this position.
function getPositionId(IERC20 collateralToken, bytes32 collectionId) internal pure returns (uint) {
return uint(keccak256(abi.encodePacked(collateralToken, collectionId)));
}
}
// File: contracts/ConditionalTokens.sol
pragma solidity ^0.5.1;
contract ConditionalTokens is ERC1155 {
/// @dev Emitted upon the successful preparation of a condition.
/// @param conditionId The condition's ID. This ID may be derived from the other three parameters via ``keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount))``.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
event ConditionPreparation(
bytes32 indexed conditionId,
address indexed oracle,
bytes32 indexed questionId,
uint outcomeSlotCount
);
event ConditionResolution(
bytes32 indexed conditionId,
address indexed oracle,
bytes32 indexed questionId,
uint outcomeSlotCount,
uint[] payoutNumerators
);
/// @dev Emitted when a position is successfully split.
event PositionSplit(
address indexed stakeholder,
IERC20 collateralToken,
bytes32 indexed parentCollectionId,
bytes32 indexed conditionId,
uint[] partition,
uint amount
);
/// @dev Emitted when positions are successfully merged.
event PositionsMerge(
address indexed stakeholder,
IERC20 collateralToken,
bytes32 indexed parentCollectionId,
bytes32 indexed conditionId,
uint[] partition,
uint amount
);
event PayoutRedemption(
address indexed redeemer,
IERC20 indexed collateralToken,
bytes32 indexed parentCollectionId,
bytes32 conditionId,
uint[] indexSets,
uint payout
);
/// Mapping key is an condition ID. Value represents numerators of the payout vector associated with the condition. This array is initialized with a length equal to the outcome slot count. E.g. Condition with 3 outcomes [A, B, C] and two of those correct [0.5, 0.5, 0]. In Ethereum there are no decimal values, so here, 0.5 is represented by fractions like 1/2 == 0.5. That's why we need numerator and denominator values. Payout numerators are also used as a check of initialization. If the numerators array is empty (has length zero), the condition was not created/prepared. See getOutcomeSlotCount.
mapping(bytes32 => uint[]) public payoutNumerators;
/// Denominator is also used for checking if the condition has been resolved. If the denominator is non-zero, then the condition has been resolved.
mapping(bytes32 => uint) public payoutDenominator;
/// @dev This function prepares a condition by initializing a payout vector associated with the condition.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
function prepareCondition(address oracle, bytes32 questionId, uint outcomeSlotCount) external {
// Limit of 256 because we use a partition array that is a number of 256 bits.
require(outcomeSlotCount <= 256, "too many outcome slots");
require(outcomeSlotCount > 1, "there should be more than one outcome slot");
bytes32 conditionId = CTHelpers.getConditionId(oracle, questionId, outcomeSlotCount);
require(payoutNumerators[conditionId].length == 0, "condition already prepared");
payoutNumerators[conditionId] = new uint[](outcomeSlotCount);
emit ConditionPreparation(conditionId, oracle, questionId, outcomeSlotCount);
}
/// @dev Called by the oracle for reporting results of conditions. Will set the payout vector for the condition with the ID ``keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount))``, where oracle is the message sender, questionId is one of the parameters of this function, and outcomeSlotCount is the length of the payouts parameter, which contains the payoutNumerators for each outcome slot of the condition.
/// @param questionId The question ID the oracle is answering for
/// @param payouts The oracle's answer
function reportPayouts(bytes32 questionId, uint[] calldata payouts) external {
uint outcomeSlotCount = payouts.length;
require(outcomeSlotCount > 1, "there should be more than one outcome slot");
// IMPORTANT, the oracle is enforced to be the sender because it's part of the hash.
bytes32 conditionId = CTHelpers.getConditionId(msg.sender, questionId, outcomeSlotCount);
require(payoutNumerators[conditionId].length == outcomeSlotCount, "condition not prepared or found");
require(payoutDenominator[conditionId] == 0, "payout denominator already set");
uint den = 0;
for (uint i = 0; i < outcomeSlotCount; i++) {
uint num = payouts[i];
den = den.add(num);
require(payoutNumerators[conditionId][i] == 0, "payout numerator already set");
payoutNumerators[conditionId][i] = num;
}
require(den > 0, "payout is all zeroes");
payoutDenominator[conditionId] = den;
emit ConditionResolution(conditionId, msg.sender, questionId, outcomeSlotCount, payoutNumerators[conditionId]);
}
/// @dev This function splits a position. If splitting from the collateral, this contract will attempt to transfer `amount` collateral from the message sender to itself. Otherwise, this contract will burn `amount` stake held by the message sender in the position being split worth of EIP 1155 tokens. Regardless, if successful, `amount` stake will be minted in the split target positions. If any of the transfers, mints, or burns fail, the transaction will revert. The transaction will also revert if the given partition is trivial, invalid, or refers to more slots than the condition is prepared with.
/// @param collateralToken The address of the positions' backing collateral token.
/// @param parentCollectionId The ID of the outcome collections common to the position being split and the split target positions. May be null, in which only the collateral is shared.
/// @param conditionId The ID of the condition to split on.
/// @param partition An array of disjoint index sets representing a nontrivial partition of the outcome slots of the given condition. E.g. A|B and C but not A|B and B|C (is not disjoint). Each element's a number which, together with the condition, represents the outcome collection. E.g. 0b110 is A|B, 0b010 is B, etc.
/// @param amount The amount of collateral or stake to split.
function splitPosition(
IERC20 collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint[] calldata partition,
uint amount
) external {
require(partition.length > 1, "got empty or singleton partition");
uint outcomeSlotCount = payoutNumerators[conditionId].length;
require(outcomeSlotCount > 0, "condition not prepared yet");
// For a condition with 4 outcomes fullIndexSet's 0b1111; for 5 it's 0b11111...
uint fullIndexSet = (1 << outcomeSlotCount) - 1;
// freeIndexSet starts as the full collection
uint freeIndexSet = fullIndexSet;
// This loop checks that all condition sets are disjoint (the same outcome is not part of more than 1 set)
uint[] memory positionIds = new uint[](partition.length);
uint[] memory amounts = new uint[](partition.length);
for (uint i = 0; i < partition.length; i++) {
uint indexSet = partition[i];
require(indexSet > 0 && indexSet < fullIndexSet, "got invalid index set");
require((indexSet & freeIndexSet) == indexSet, "partition not disjoint");
freeIndexSet ^= indexSet;
positionIds[i] = CTHelpers.getPositionId(collateralToken, CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet));
amounts[i] = amount;
}
if (freeIndexSet == 0) {
// Partitioning the full set of outcomes for the condition in this branch
if (parentCollectionId == bytes32(0)) {
require(collateralToken.transferFrom(msg.sender, address(this), amount), "could not receive collateral tokens");
} else {
_burn(
msg.sender,
CTHelpers.getPositionId(collateralToken, parentCollectionId),
amount
);
}
} else {
// Partitioning a subset of outcomes for the condition in this branch.
// For example, for a condition with three outcomes A, B, and C, this branch
// allows the splitting of a position $:(A|C) to positions $:(A) and $:(C).
_burn(
msg.sender,
CTHelpers.getPositionId(collateralToken,
CTHelpers.getCollectionId(parentCollectionId, conditionId, fullIndexSet ^ freeIndexSet)),
amount
);
}
_batchMint(
msg.sender,
// position ID is the ERC 1155 token ID
positionIds,
amounts,
""
);
emit PositionSplit(msg.sender, collateralToken, parentCollectionId, conditionId, partition, amount);
}
function mergePositions(
IERC20 collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint[] calldata partition,
uint amount
) external {
require(partition.length > 1, "got empty or singleton partition");
uint outcomeSlotCount = payoutNumerators[conditionId].length;
require(outcomeSlotCount > 0, "condition not prepared yet");
uint fullIndexSet = (1 << outcomeSlotCount) - 1;
uint freeIndexSet = fullIndexSet;
uint[] memory positionIds = new uint[](partition.length);
uint[] memory amounts = new uint[](partition.length);
for (uint i = 0; i < partition.length; i++) {
uint indexSet = partition[i];
require(indexSet > 0 && indexSet < fullIndexSet, "got invalid index set");
require((indexSet & freeIndexSet) == indexSet, "partition not disjoint");
freeIndexSet ^= indexSet;
positionIds[i] = CTHelpers.getPositionId(collateralToken, CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet));
amounts[i] = amount;
}
_batchBurn(
msg.sender,
positionIds,
amounts
);
if (freeIndexSet == 0) {
if (parentCollectionId == bytes32(0)) {
require(collateralToken.transfer(msg.sender, amount), "could not send collateral tokens");
} else {
_mint(
msg.sender,
CTHelpers.getPositionId(collateralToken, parentCollectionId),
amount,
""
);
}
} else {
_mint(
msg.sender,
CTHelpers.getPositionId(collateralToken,
CTHelpers.getCollectionId(parentCollectionId, conditionId, fullIndexSet ^ freeIndexSet)),
amount,
""
);
}
emit PositionsMerge(msg.sender, collateralToken, parentCollectionId, conditionId, partition, amount);
}
function redeemPositions(IERC20 collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] calldata indexSets) external {
uint den = payoutDenominator[conditionId];
require(den > 0, "result for condition not received yet");
uint outcomeSlotCount = payoutNumerators[conditionId].length;
require(outcomeSlotCount > 0, "condition not prepared yet");
uint totalPayout = 0;
uint fullIndexSet = (1 << outcomeSlotCount) - 1;
for (uint i = 0; i < indexSets.length; i++) {
uint indexSet = indexSets[i];
require(indexSet > 0 && indexSet < fullIndexSet, "got invalid index set");
uint positionId = CTHelpers.getPositionId(collateralToken,
CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet));
uint payoutNumerator = 0;
for (uint j = 0; j < outcomeSlotCount; j++) {
if (indexSet & (1 << j) != 0) {
payoutNumerator = payoutNumerator.add(payoutNumerators[conditionId][j]);
}
}
uint payoutStake = balanceOf(msg.sender, positionId);
if (payoutStake > 0) {
totalPayout = totalPayout.add(payoutStake.mul(payoutNumerator).div(den));
_burn(msg.sender, positionId, payoutStake);
}
}
if (totalPayout > 0) {
if (parentCollectionId == bytes32(0)) {
require(collateralToken.transfer(msg.sender, totalPayout), "could not transfer payout to message sender");
} else {
_mint(msg.sender, CTHelpers.getPositionId(collateralToken, parentCollectionId), totalPayout, "");
}
}
emit PayoutRedemption(msg.sender, collateralToken, parentCollectionId, conditionId, indexSets, totalPayout);
}
/// @dev Gets the outcome slot count of a condition.
/// @param conditionId ID of the condition.
/// @return Number of outcome slots associated with a condition, or zero if condition has not been prepared yet.
function getOutcomeSlotCount(bytes32 conditionId) external view returns (uint) {
return payoutNumerators[conditionId].length;
}
/// @dev Constructs a condition ID from an oracle, a question ID, and the outcome slot count for the question.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
function getConditionId(address oracle, bytes32 questionId, uint outcomeSlotCount) external pure returns (bytes32) {
return CTHelpers.getConditionId(oracle, questionId, outcomeSlotCount);
}
/// @dev Constructs an outcome collection ID from a parent collection and an outcome collection.
/// @param parentCollectionId Collection ID of the parent outcome collection, or bytes32(0) if there's no parent.
/// @param conditionId Condition ID of the outcome collection to combine with the parent outcome collection.
/// @param indexSet Index set of the outcome collection to combine with the parent outcome collection.
function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint indexSet) external view returns (bytes32) {
return CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet);
}
/// @dev Constructs a position ID from a collateral token and an outcome collection. These IDs are used as the ERC-1155 ID for this contract.
/// @param collateralToken Collateral token which backs the position.
/// @param collectionId ID of the outcome collection associated with this position.
function getPositionId(IERC20 collateralToken, bytes32 collectionId) external pure returns (uint) {
return CTHelpers.getPositionId(collateralToken, collectionId);
}
}File 3 of 5: GnosisSafe
pragma solidity >=0.5.0 <0.7.0;
/// @title SelfAuthorized - authorizes current contract to perform actions
/// @author Richard Meissner - <richard@gnosis.pm>
contract SelfAuthorized {
modifier authorized() {
require(msg.sender == address(this), "Method can only be called from this contract");
_;
}
}
/// @title MasterCopy - Base for master copy contracts (should always be first super contract)
/// This contract is tightly coupled to our proxy contract (see `proxies/Proxy.sol`)
/// @author Richard Meissner - <richard@gnosis.io>
contract MasterCopy is SelfAuthorized {
event ChangedMasterCopy(address masterCopy);
// masterCopy always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract.
// It should also always be ensured that the address is stored alone (uses a full word)
address private masterCopy;
/// @dev Allows to upgrade the contract. This can only be done via a Safe transaction.
/// @param _masterCopy New contract address.
function changeMasterCopy(address _masterCopy)
public
authorized
{
// Master copy address cannot be null.
require(_masterCopy != address(0), "Invalid master copy address provided");
masterCopy = _masterCopy;
emit ChangedMasterCopy(_masterCopy);
}
}
/// @title Module - Base class for modules.
/// @author Stefan George - <stefan@gnosis.pm>
/// @author Richard Meissner - <richard@gnosis.pm>
contract Module is MasterCopy {
ModuleManager public manager;
modifier authorized() {
require(msg.sender == address(manager), "Method can only be called from manager");
_;
}
function setManager()
internal
{
// manager can only be 0 at initalization of contract.
// Check ensures that setup function can only be called once.
require(address(manager) == address(0), "Manager has already been set");
manager = ModuleManager(msg.sender);
}
}
/// @title Enum - Collection of enums
/// @author Richard Meissner - <richard@gnosis.pm>
contract Enum {
enum Operation {
Call,
DelegateCall
}
}
/// @title Executor - A contract that can execute transactions
/// @author Richard Meissner - <richard@gnosis.pm>
contract Executor {
function execute(address to, uint256 value, bytes memory data, Enum.Operation operation, uint256 txGas)
internal
returns (bool success)
{
if (operation == Enum.Operation.Call)
success = executeCall(to, value, data, txGas);
else if (operation == Enum.Operation.DelegateCall)
success = executeDelegateCall(to, data, txGas);
else
success = false;
}
function executeCall(address to, uint256 value, bytes memory data, uint256 txGas)
internal
returns (bool success)
{
// solium-disable-next-line security/no-inline-assembly
assembly {
success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
}
}
function executeDelegateCall(address to, bytes memory data, uint256 txGas)
internal
returns (bool success)
{
// solium-disable-next-line security/no-inline-assembly
assembly {
success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
}
}
}
/// @title SecuredTokenTransfer - Secure token transfer
/// @author Richard Meissner - <richard@gnosis.pm>
contract SecuredTokenTransfer {
/// @dev Transfers a token and returns if it was a success
/// @param token Token that should be transferred
/// @param receiver Receiver to whom the token should be transferred
/// @param amount The amount of tokens that should be transferred
function transferToken (
address token,
address receiver,
uint256 amount
)
internal
returns (bool transferred)
{
bytes memory data = abi.encodeWithSignature("transfer(address,uint256)", receiver, amount);
// solium-disable-next-line security/no-inline-assembly
assembly {
let success := call(sub(gas, 10000), token, 0, add(data, 0x20), mload(data), 0, 0)
let ptr := mload(0x40)
mstore(0x40, add(ptr, returndatasize()))
returndatacopy(ptr, 0, returndatasize())
switch returndatasize()
case 0 { transferred := success }
case 0x20 { transferred := iszero(or(iszero(success), iszero(mload(ptr)))) }
default { transferred := 0 }
}
}
}
/// @title Module Manager - A contract that manages modules that can execute transactions via this contract
/// @author Stefan George - <stefan@gnosis.pm>
/// @author Richard Meissner - <richard@gnosis.pm>
contract ModuleManager is SelfAuthorized, Executor {
event EnabledModule(Module module);
event DisabledModule(Module module);
event ExecutionFromModuleSuccess(address indexed module);
event ExecutionFromModuleFailure(address indexed module);
address internal constant SENTINEL_MODULES = address(0x1);
mapping (address => address) internal modules;
function setupModules(address to, bytes memory data)
internal
{
require(modules[SENTINEL_MODULES] == address(0), "Modules have already been initialized");
modules[SENTINEL_MODULES] = SENTINEL_MODULES;
if (to != address(0))
// Setup has to complete successfully or transaction fails.
require(executeDelegateCall(to, data, gasleft()), "Could not finish initialization");
}
/// @dev Allows to add a module to the whitelist.
/// This can only be done via a Safe transaction.
/// @param module Module to be whitelisted.
function enableModule(Module module)
public
authorized
{
// Module address cannot be null or sentinel.
require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided");
// Module cannot be added twice.
require(modules[address(module)] == address(0), "Module has already been added");
modules[address(module)] = modules[SENTINEL_MODULES];
modules[SENTINEL_MODULES] = address(module);
emit EnabledModule(module);
}
/// @dev Allows to remove a module from the whitelist.
/// This can only be done via a Safe transaction.
/// @param prevModule Module that pointed to the module to be removed in the linked list
/// @param module Module to be removed.
function disableModule(Module prevModule, Module module)
public
authorized
{
// Validate module address and check that it corresponds to module index.
require(address(module) != address(0) && address(module) != SENTINEL_MODULES, "Invalid module address provided");
require(modules[address(prevModule)] == address(module), "Invalid prevModule, module pair provided");
modules[address(prevModule)] = modules[address(module)];
modules[address(module)] = address(0);
emit DisabledModule(module);
}
/// @dev Allows a Module to execute a Safe transaction without any further confirmations.
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction.
function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation)
public
returns (bool success)
{
// Only whitelisted modules are allowed.
require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "Method can only be called from an enabled module");
// Execute transaction without further confirmations.
success = execute(to, value, data, operation, gasleft());
if (success) emit ExecutionFromModuleSuccess(msg.sender);
else emit ExecutionFromModuleFailure(msg.sender);
}
/// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction.
function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation)
public
returns (bool success, bytes memory returnData)
{
success = execTransactionFromModule(to, value, data, operation);
// solium-disable-next-line security/no-inline-assembly
assembly {
// Load free memory location
let ptr := mload(0x40)
// We allocate memory for the return data by setting the free memory location to
// current free memory location + data size + 32 bytes for data size value
mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
// Store the size
mstore(ptr, returndatasize())
// Store the data
returndatacopy(add(ptr, 0x20), 0, returndatasize())
// Point the return data to the correct memory location
returnData := ptr
}
}
/// @dev Returns array of first 10 modules.
/// @return Array of modules.
function getModules()
public
view
returns (address[] memory)
{
(address[] memory array,) = getModulesPaginated(SENTINEL_MODULES, 10);
return array;
}
/// @dev Returns array of modules.
/// @param start Start of the page.
/// @param pageSize Maximum number of modules that should be returned.
/// @return Array of modules.
function getModulesPaginated(address start, uint256 pageSize)
public
view
returns (address[] memory array, address next)
{
// Init array with max page size
array = new address[](pageSize);
// Populate return array
uint256 moduleCount = 0;
address currentModule = modules[start];
while(currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
array[moduleCount] = currentModule;
currentModule = modules[currentModule];
moduleCount++;
}
next = currentModule;
// Set correct size of returned array
// solium-disable-next-line security/no-inline-assembly
assembly {
mstore(array, moduleCount)
}
}
}
/// @title OwnerManager - Manages a set of owners and a threshold to perform actions.
/// @author Stefan George - <stefan@gnosis.pm>
/// @author Richard Meissner - <richard@gnosis.pm>
contract OwnerManager is SelfAuthorized {
event AddedOwner(address owner);
event RemovedOwner(address owner);
event ChangedThreshold(uint256 threshold);
address internal constant SENTINEL_OWNERS = address(0x1);
mapping(address => address) internal owners;
uint256 ownerCount;
uint256 internal threshold;
/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
function setupOwners(address[] memory _owners, uint256 _threshold)
internal
{
// Threshold can only be 0 at initialization.
// Check ensures that setup function can only be called once.
require(threshold == 0, "Owners have already been setup");
// Validate that threshold is smaller than number of added owners.
require(_threshold <= _owners.length, "Threshold cannot exceed owner count");
// There has to be at least one Safe owner.
require(_threshold >= 1, "Threshold needs to be greater than 0");
// Initializing Safe owners.
address currentOwner = SENTINEL_OWNERS;
for (uint256 i = 0; i < _owners.length; i++) {
// Owner address cannot be null.
address owner = _owners[i];
require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
// No duplicate owners allowed.
require(owners[owner] == address(0), "Duplicate owner address provided");
owners[currentOwner] = owner;
currentOwner = owner;
}
owners[currentOwner] = SENTINEL_OWNERS;
ownerCount = _owners.length;
threshold = _threshold;
}
/// @dev Allows to add a new owner to the Safe and update the threshold at the same time.
/// This can only be done via a Safe transaction.
/// @param owner New owner address.
/// @param _threshold New threshold.
function addOwnerWithThreshold(address owner, uint256 _threshold)
public
authorized
{
// Owner address cannot be null.
require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
// No duplicate owners allowed.
require(owners[owner] == address(0), "Address is already an owner");
owners[owner] = owners[SENTINEL_OWNERS];
owners[SENTINEL_OWNERS] = owner;
ownerCount++;
emit AddedOwner(owner);
// Change threshold if threshold was changed.
if (threshold != _threshold)
changeThreshold(_threshold);
}
/// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
/// This can only be done via a Safe transaction.
/// @param prevOwner Owner that pointed to the owner to be removed in the linked list
/// @param owner Owner address to be removed.
/// @param _threshold New threshold.
function removeOwner(address prevOwner, address owner, uint256 _threshold)
public
authorized
{
// Only allow to remove an owner, if threshold can still be reached.
require(ownerCount - 1 >= _threshold, "New owner count needs to be larger than new threshold");
// Validate owner address and check that it corresponds to owner index.
require(owner != address(0) && owner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[prevOwner] == owner, "Invalid prevOwner, owner pair provided");
owners[prevOwner] = owners[owner];
owners[owner] = address(0);
ownerCount--;
emit RemovedOwner(owner);
// Change threshold if threshold was changed.
if (threshold != _threshold)
changeThreshold(_threshold);
}
/// @dev Allows to swap/replace an owner from the Safe with another address.
/// This can only be done via a Safe transaction.
/// @param prevOwner Owner that pointed to the owner to be replaced in the linked list
/// @param oldOwner Owner address to be replaced.
/// @param newOwner New owner address.
function swapOwner(address prevOwner, address oldOwner, address newOwner)
public
authorized
{
// Owner address cannot be null.
require(newOwner != address(0) && newOwner != SENTINEL_OWNERS, "Invalid owner address provided");
// No duplicate owners allowed.
require(owners[newOwner] == address(0), "Address is already an owner");
// Validate oldOwner address and check that it corresponds to owner index.
require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "Invalid owner address provided");
require(owners[prevOwner] == oldOwner, "Invalid prevOwner, owner pair provided");
owners[newOwner] = owners[oldOwner];
owners[prevOwner] = newOwner;
owners[oldOwner] = address(0);
emit RemovedOwner(oldOwner);
emit AddedOwner(newOwner);
}
/// @dev Allows to update the number of required confirmations by Safe owners.
/// This can only be done via a Safe transaction.
/// @param _threshold New threshold.
function changeThreshold(uint256 _threshold)
public
authorized
{
// Validate that threshold is smaller than number of owners.
require(_threshold <= ownerCount, "Threshold cannot exceed owner count");
// There has to be at least one Safe owner.
require(_threshold >= 1, "Threshold needs to be greater than 0");
threshold = _threshold;
emit ChangedThreshold(threshold);
}
function getThreshold()
public
view
returns (uint256)
{
return threshold;
}
function isOwner(address owner)
public
view
returns (bool)
{
return owner != SENTINEL_OWNERS && owners[owner] != address(0);
}
/// @dev Returns array of owners.
/// @return Array of Safe owners.
function getOwners()
public
view
returns (address[] memory)
{
address[] memory array = new address[](ownerCount);
// populate return array
uint256 index = 0;
address currentOwner = owners[SENTINEL_OWNERS];
while(currentOwner != SENTINEL_OWNERS) {
array[index] = currentOwner;
currentOwner = owners[currentOwner];
index ++;
}
return array;
}
}
/// @title Fallback Manager - A contract that manages fallback calls made to this contract
/// @author Richard Meissner - <richard@gnosis.pm>
contract FallbackManager is SelfAuthorized {
// keccak256("fallback_manager.handler.address")
bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;
function internalSetFallbackHandler(address handler) internal {
bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
// solium-disable-next-line security/no-inline-assembly
assembly {
sstore(slot, handler)
}
}
/// @dev Allows to add a contract to handle fallback calls.
/// Only fallback calls without value and with data will be forwarded.
/// This can only be done via a Safe transaction.
/// @param handler contract to handle fallbacks calls.
function setFallbackHandler(address handler)
public
authorized
{
internalSetFallbackHandler(handler);
}
function ()
external
payable
{
// Only calls without value and with data will be forwarded
if (msg.value > 0 || msg.data.length == 0) {
return;
}
bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
address handler;
// solium-disable-next-line security/no-inline-assembly
assembly {
handler := sload(slot)
}
if (handler != address(0)) {
// solium-disable-next-line security/no-inline-assembly
assembly {
calldatacopy(0, 0, calldatasize())
let success := call(gas, handler, 0, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if eq(success, 0) { revert(0, returndatasize()) }
return(0, returndatasize())
}
}
}
}
/// @title SignatureDecoder - Decodes signatures that a encoded as bytes
/// @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
/// @author Richard Meissner - <richard@gnosis.pm>
contract SignatureDecoder {
/// @dev Recovers address who signed the message
/// @param messageHash operation ethereum signed message hash
/// @param messageSignature message `txHash` signature
/// @param pos which signature to read
function recoverKey (
bytes32 messageHash,
bytes memory messageSignature,
uint256 pos
)
internal
pure
returns (address)
{
uint8 v;
bytes32 r;
bytes32 s;
(v, r, s) = signatureSplit(messageSignature, pos);
return ecrecover(messageHash, v, r, s);
}
/// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`.
/// @notice Make sure to peform a bounds check for @param pos, to avoid out of bounds access on @param signatures
/// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access
/// @param signatures concatenated rsv signatures
function signatureSplit(bytes memory signatures, uint256 pos)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
// The signature format is a compact form of:
// {bytes32 r}{bytes32 s}{uint8 v}
// Compact means, uint8 is not padded to 32 bytes.
// solium-disable-next-line security/no-inline-assembly
assembly {
let signaturePos := mul(0x41, pos)
r := mload(add(signatures, add(signaturePos, 0x20)))
s := mload(add(signatures, add(signaturePos, 0x40)))
// Here we are loading the last 32 bytes, including 31 bytes
// of 's'. There is no 'mload8' to do this.
//
// 'byte' is not working due to the Solidity parser, so lets
// use the second best option, 'and'
v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
}
}
}
contract ISignatureValidatorConstants {
// bytes4(keccak256("isValidSignature(bytes,bytes)")
bytes4 constant internal EIP1271_MAGIC_VALUE = 0x20c13b0b;
}
contract ISignatureValidator is ISignatureValidatorConstants {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param _data Arbitrary length data signed on the behalf of address(this)
* @param _signature Signature byte array associated with _data
*
* MUST return the bytes4 magic value 0x20c13b0b when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
* MUST allow external calls
*/
function isValidSignature(
bytes memory _data,
bytes memory _signature)
public
view
returns (bytes4);
}
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
* TODO: remove once open zeppelin update to solc 0.5.0
*/
library SafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
/// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191.
/// @author Stefan George - <stefan@gnosis.io>
/// @author Richard Meissner - <richard@gnosis.io>
/// @author Ricardo Guilherme Schmidt - (Status Research & Development GmbH) - Gas Token Payment
contract GnosisSafe
is MasterCopy, ModuleManager, OwnerManager, SignatureDecoder, SecuredTokenTransfer, ISignatureValidatorConstants, FallbackManager {
using SafeMath for uint256;
string public constant NAME = "Gnosis Safe";
string public constant VERSION = "1.1.1";
//keccak256(
// "EIP712Domain(address verifyingContract)"
//);
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749;
//keccak256(
// "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
//);
bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
//keccak256(
// "SafeMessage(bytes message)"
//);
bytes32 private constant SAFE_MSG_TYPEHASH = 0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca;
event ApproveHash(
bytes32 indexed approvedHash,
address indexed owner
);
event SignMsg(
bytes32 indexed msgHash
);
event ExecutionFailure(
bytes32 txHash, uint256 payment
);
event ExecutionSuccess(
bytes32 txHash, uint256 payment
);
uint256 public nonce;
bytes32 public domainSeparator;
// Mapping to keep track of all message hashes that have been approve by ALL REQUIRED owners
mapping(bytes32 => uint256) public signedMessages;
// Mapping to keep track of all hashes (message or transaction) that have been approve by ANY owners
mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
// This constructor ensures that this contract can only be used as a master copy for Proxy contracts
constructor() public {
// By setting the threshold it is not possible to call setup anymore,
// so we create a Safe with 0 owners and threshold 1.
// This is an unusable Safe, perfect for the mastercopy
threshold = 1;
}
/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
/// @param to Contract address for optional delegate call.
/// @param data Data payload for optional delegate call.
/// @param fallbackHandler Handler for fallback calls to this contract
/// @param paymentToken Token that should be used for the payment (0 is ETH)
/// @param payment Value that should be paid
/// @param paymentReceiver Adddress that should receive the payment (or 0 if tx.origin)
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
)
external
{
require(domainSeparator == 0, "Domain Separator already set!");
domainSeparator = keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, this));
setupOwners(_owners, _threshold);
if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
// As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
setupModules(to, data);
if (payment > 0) {
// To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
// baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
}
}
/// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
/// Note: The fees are always transfered, even if the user transaction fails.
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @param safeTxGas Gas that should be used for the Safe transaction.
/// @param baseGas Gas costs for that are indipendent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
/// @param gasPrice Gas price that should be used for the payment calculation.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes calldata signatures
)
external
returns (bool success)
{
bytes32 txHash;
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
bytes memory txHashData = encodeTransactionData(
to, value, data, operation, // Transaction info
safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, // Payment info
nonce
);
// Increase nonce and execute transaction.
nonce++;
txHash = keccak256(txHashData);
checkSignatures(txHash, txHashData, signatures, true);
}
require(gasleft() >= safeTxGas, "Not enough gas to execute safe transaction");
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
uint256 gasUsed = gasleft();
// If no safeTxGas has been set and the gasPrice is 0 we assume that all available gas can be used
success = execute(to, value, data, operation, safeTxGas == 0 && gasPrice == 0 ? gasleft() : safeTxGas);
gasUsed = gasUsed.sub(gasleft());
// We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
uint256 payment = 0;
if (gasPrice > 0) {
payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
}
if (success) emit ExecutionSuccess(txHash, payment);
else emit ExecutionFailure(txHash, payment);
}
}
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver
)
private
returns (uint256 payment)
{
// solium-disable-next-line security/no-tx-origin
address payable receiver = refundReceiver == address(0) ? tx.origin : refundReceiver;
if (gasToken == address(0)) {
// For ETH we will only adjust the gas price to not be higher than the actual used gas price
payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
// solium-disable-next-line security/no-send
require(receiver.send(payment), "Could not pay gas costs with ether");
} else {
payment = gasUsed.add(baseGas).mul(gasPrice);
require(transferToken(gasToken, receiver, payment), "Could not pay gas costs with token");
}
}
/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash)
* @param data That should be signed (this is passed to an external validator contract)
* @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
* @param consumeHash Indicates that in case of an approved hash the storage can be freed to save gas
*/
function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures, bool consumeHash)
internal
{
// Load threshold to avoid multiple storage loads
uint256 _threshold = threshold;
// Check that a threshold is set
require(_threshold > 0, "Threshold needs to be defined!");
// Check that the provided signature data is not too short
require(signatures.length >= _threshold.mul(65), "Signatures data too short");
// There cannot be an owner with address 0.
address lastOwner = address(0);
address currentOwner;
uint8 v;
bytes32 r;
bytes32 s;
uint256 i;
for (i = 0; i < _threshold; i++) {
(v, r, s) = signatureSplit(signatures, i);
// If v is 0 then it is a contract signature
if (v == 0) {
// When handling contract signatures the address of the contract is encoded into r
currentOwner = address(uint256(r));
// Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
// This check is not completely accurate, since it is possible that more signatures than the threshold are send.
// Here we only check that the pointer is not pointing inside the part that is being processed
require(uint256(s) >= _threshold.mul(65), "Invalid contract signature location: inside static part");
// Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
require(uint256(s).add(32) <= signatures.length, "Invalid contract signature location: length not present");
// Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
uint256 contractSignatureLen;
// solium-disable-next-line security/no-inline-assembly
assembly {
contractSignatureLen := mload(add(add(signatures, s), 0x20))
}
require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "Invalid contract signature location: data not complete");
// Check signature
bytes memory contractSignature;
// solium-disable-next-line security/no-inline-assembly
assembly {
// The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
contractSignature := add(add(signatures, s), 0x20)
}
require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "Invalid contract signature provided");
// If v is 1 then it is an approved hash
} else if (v == 1) {
// When handling approved hashes the address of the approver is encoded into r
currentOwner = address(uint256(r));
// Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "Hash has not been approved");
// Hash has been marked for consumption. If this hash was pre-approved free storage
if (consumeHash && msg.sender != currentOwner) {
approvedHashes[currentOwner][dataHash] = 0;
}
} else if (v > 30) {
// To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover
currentOwner = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
} else {
// Use ecrecover with the messageHash for EOA signatures
currentOwner = ecrecover(dataHash, v, r, s);
}
require (
currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS,
"Invalid owner provided"
);
lastOwner = currentOwner;
}
}
/// @dev Allows to estimate a Safe transaction.
/// This method is only meant for estimation purpose, therefore two different protection mechanism against execution in a transaction have been made:
/// 1.) The method can only be called from the safe itself
/// 2.) The response is returned with a revert
/// When estimating set `from` to the address of the safe.
/// Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
function requiredTxGas(address to, uint256 value, bytes calldata data, Enum.Operation operation)
external
authorized
returns (uint256)
{
uint256 startGas = gasleft();
// We don't provide an error message here, as we use it to return the estimate
// solium-disable-next-line error-reason
require(execute(to, value, data, operation, gasleft()));
uint256 requiredGas = startGas - gasleft();
// Convert response to string and return via error message
revert(string(abi.encodePacked(requiredGas)));
}
/**
* @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature.
* @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract.
*/
function approveHash(bytes32 hashToApprove)
external
{
require(owners[msg.sender] != address(0), "Only owners can approve a hash");
approvedHashes[msg.sender][hashToApprove] = 1;
emit ApproveHash(hashToApprove, msg.sender);
}
/**
* @dev Marks a message as signed
* @param _data Arbitrary length data that should be marked as signed on the behalf of address(this)
*/
function signMessage(bytes calldata _data)
external
authorized
{
bytes32 msgHash = getMessageHash(_data);
signedMessages[msgHash] = 1;
emit SignMsg(msgHash);
}
/**
* Implementation of ISignatureValidator (see `interfaces/ISignatureValidator.sol`)
* @dev Should return whether the signature provided is valid for the provided data.
* The save does not implement the interface since `checkSignatures` is not a view method.
* The method will not perform any state changes (see parameters of `checkSignatures`)
* @param _data Arbitrary length data signed on the behalf of address(this)
* @param _signature Signature byte array associated with _data
* @return a bool upon valid or invalid signature with corresponding _data
*/
function isValidSignature(bytes calldata _data, bytes calldata _signature)
external
returns (bytes4)
{
bytes32 messageHash = getMessageHash(_data);
if (_signature.length == 0) {
require(signedMessages[messageHash] != 0, "Hash not approved");
} else {
// consumeHash needs to be false, as the state should not be changed
checkSignatures(messageHash, _data, _signature, false);
}
return EIP1271_MAGIC_VALUE;
}
/// @dev Returns hash of a message that can be signed by owners.
/// @param message Message that should be hashed
/// @return Message hash.
function getMessageHash(
bytes memory message
)
public
view
returns (bytes32)
{
bytes32 safeMessageHash = keccak256(
abi.encode(SAFE_MSG_TYPEHASH, keccak256(message))
);
return keccak256(
abi.encodePacked(byte(0x19), byte(0x01), domainSeparator, safeMessageHash)
);
}
/// @dev Returns the bytes that are hashed to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Fas that should be used for the safe transaction.
/// @param baseGas Gas costs for data used to trigger the safe transaction.
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash bytes.
function encodeTransactionData(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
)
public
view
returns (bytes memory)
{
bytes32 safeTxHash = keccak256(
abi.encode(SAFE_TX_TYPEHASH, to, value, keccak256(data), operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce)
);
return abi.encodePacked(byte(0x19), byte(0x01), domainSeparator, safeTxHash);
}
/// @dev Returns hash to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Fas that should be used for the safe transaction.
/// @param baseGas Gas costs for data used to trigger the safe transaction.
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash.
function getTransactionHash(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
)
public
view
returns (bytes32)
{
return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
}
}File 4 of 5: MultiSend
pragma solidity >=0.5.0 <0.7.0;
contract MultiSend {
/// @dev Sends multiple transactions and reverts all if one fails.
/// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of
/// operation as a uint8 with 0 for a call or 1 for a delegatecall (=> 1 byte),
/// to as a address (=> 20 bytes),
/// value as a uint256 (=> 32 bytes),
/// data length as a uint256 (=> 32 bytes),
/// data as bytes.
/// see abi.encodePacked for more information on packed encoding
function multiSend(bytes memory transactions)
public
{
// solium-disable-next-line security/no-inline-assembly
assembly {
let length := mload(transactions)
let i := 0x20
for { } lt(i, length) { } {
// First byte of the data is the operation.
// We shift by 248 bits (256 - 8 [operation byte]) it right since mload will always load 32 bytes (a word).
// This will also zero out unused data.
let operation := shr(0xf8, mload(add(transactions, i)))
// We offset the load address by 1 byte (operation byte)
// We shift it right by 96 bits (256 - 160 [20 address bytes]) to right-align the data and zero out unused data.
let to := shr(0x60, mload(add(transactions, add(i, 0x01))))
// We offset the load address by 21 byte (operation byte + 20 address bytes)
let value := mload(add(transactions, add(i, 0x15)))
// We offset the load address by 53 byte (operation byte + 20 address bytes + 32 value bytes)
let dataLength := mload(add(transactions, add(i, 0x35)))
// We offset the load address by 85 byte (operation byte + 20 address bytes + 32 value bytes + 32 data length bytes)
let data := add(transactions, add(i, 0x55))
let success := 0
switch operation
case 0 { success := call(gas, to, value, data, dataLength, 0, 0) }
case 1 { success := delegatecall(gas, to, data, dataLength, 0, 0) }
if eq(success, 0) { revert(0, 0) }
// Next entry starts at 85 byte + data length
i := add(i, add(0x55, dataLength))
}
}
}
}File 5 of 5: FixedProductMarketMaker
/**
*Submitted for verification at Etherscan.io on 2020-03-16
*/
// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: openzeppelin-solidity/contracts/introspection/IERC165.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-165).
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others (`ERC165Checker`).
*
* For an implementation, see `ERC165`.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: @gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/IERC1155.sol
pragma solidity ^0.5.0;
/**
@title ERC-1155 Multi Token Standard basic interface
@dev See https://eips.ethereum.org/EIPS/eip-1155
*/
contract IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address owner, uint256 id) public view returns (uint256);
function balanceOfBatch(address[] memory owners, uint256[] memory ids) public view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external;
}
// File: @gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/IERC1155TokenReceiver.sol
pragma solidity ^0.5.0;
/**
@title ERC-1155 Multi Token Receiver Interface
@dev See https://eips.ethereum.org/EIPS/eip-1155
*/
contract IERC1155TokenReceiver is IERC165 {
/**
@dev Handles the receipt of a single ERC1155 token type. This function is
called at the end of a `safeTransferFrom` after the balance has been updated.
To accept the transfer, this must return
`bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
(i.e. 0xf23a6e61, or its own function selector).
@param operator The address which initiated the transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param id The ID of the token being transferred
@param value The amount of tokens being transferred
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
)
external
returns(bytes4);
/**
@dev Handles the receipt of a multiple ERC1155 token types. This function
is called at the end of a `safeBatchTransferFrom` after the balances have
been updated. To accept the transfer(s), this must return
`bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
(i.e. 0xbc197c81, or its own function selector).
@param operator The address which initiated the batch transfer (i.e. msg.sender)
@param from The address which previously owned the token
@param ids An array containing ids of each token being transferred (order and length must match values array)
@param values An array containing amounts of each token being transferred (order and length must match ids array)
@param data Additional data with no specified format
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
returns(bytes4);
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: openzeppelin-solidity/contracts/utils/Address.sol
pragma solidity ^0.5.0;
/**
* @dev Collection of functions related to the address type,
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
// File: openzeppelin-solidity/contracts/introspection/ERC165.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the `IERC165` interface.
*
* Contracts may inherit from this and call `_registerInterface` to declare
* their support of an interface.
*/
contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See `IERC165.supportsInterface`.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See `IERC165.supportsInterface`.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
// File: @gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/ERC1155.sol
pragma solidity ^0.5.0;
/**
* @title Standard ERC1155 token
*
* @dev Implementation of the basic standard multi-token.
* See https://eips.ethereum.org/EIPS/eip-1155
* Originally based on code by Enjin: https://github.com/enjin/erc-1155
*/
contract ERC1155 is ERC165, IERC1155
{
using SafeMath for uint256;
using Address for address;
// Mapping from token ID to owner balances
mapping (uint256 => mapping(address => uint256)) private _balances;
// Mapping from owner to operator approvals
mapping (address => mapping(address => bool)) private _operatorApprovals;
constructor()
public
{
_registerInterface(
ERC1155(0).safeTransferFrom.selector ^
ERC1155(0).safeBatchTransferFrom.selector ^
ERC1155(0).balanceOf.selector ^
ERC1155(0).balanceOfBatch.selector ^
ERC1155(0).setApprovalForAll.selector ^
ERC1155(0).isApprovedForAll.selector
);
}
/**
@dev Get the specified address' balance for token with specified ID.
@param owner The address of the token holder
@param id ID of the token
@return The owner's balance of the token type requested
*/
function balanceOf(address owner, uint256 id) public view returns (uint256) {
require(owner != address(0), "ERC1155: balance query for the zero address");
return _balances[id][owner];
}
/**
@dev Get the balance of multiple account/token pairs
@param owners The addresses of the token holders
@param ids IDs of the tokens
@return Balances for each owner and token id pair
*/
function balanceOfBatch(
address[] memory owners,
uint256[] memory ids
)
public
view
returns (uint256[] memory)
{
require(owners.length == ids.length, "ERC1155: owners and IDs must have same lengths");
uint256[] memory batchBalances = new uint256[](owners.length);
for (uint256 i = 0; i < owners.length; ++i) {
require(owners[i] != address(0), "ERC1155: some address in batch balance query is zero");
batchBalances[i] = _balances[ids[i]][owners[i]];
}
return batchBalances;
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address operator, bool approved) external {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
/**
@notice Queries the approval status of an operator for a given owner.
@param owner The owner of the Tokens
@param operator Address of authorized operator
@return True if the operator is approved, false if not
*/
function isApprovedForAll(address owner, address operator) external view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
@dev Transfers `value` amount of an `id` from the `from` address to the `to` address specified.
Caller must be approved to manage the tokens being transferred out of the `from` account.
If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately.
@param from Source address
@param to Target address
@param id ID of the token type
@param value Transfer amount
@param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 value,
bytes calldata data
)
external
{
require(to != address(0), "ERC1155: target address must be non-zero");
require(
from == msg.sender || _operatorApprovals[from][msg.sender] == true,
"ERC1155: need operator approval for 3rd party transfers."
);
_balances[id][from] = _balances[id][from].sub(value);
_balances[id][to] = value.add(_balances[id][to]);
emit TransferSingle(msg.sender, from, to, id, value);
_doSafeTransferAcceptanceCheck(msg.sender, from, to, id, value, data);
}
/**
@dev Transfers `values` amount(s) of `ids` from the `from` address to the
`to` address specified. Caller must be approved to manage the tokens being
transferred out of the `from` account. If `to` is a smart contract, will
call `onERC1155BatchReceived` on `to` and act appropriately.
@param from Source address
@param to Target address
@param ids IDs of each token type
@param values Transfer amounts per token type
@param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
{
require(ids.length == values.length, "ERC1155: IDs and values must have same lengths");
require(to != address(0), "ERC1155: target address must be non-zero");
require(
from == msg.sender || _operatorApprovals[from][msg.sender] == true,
"ERC1155: need operator approval for 3rd party transfers."
);
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 value = values[i];
_balances[id][from] = _balances[id][from].sub(value);
_balances[id][to] = value.add(_balances[id][to]);
}
emit TransferBatch(msg.sender, from, to, ids, values);
_doSafeBatchTransferAcceptanceCheck(msg.sender, from, to, ids, values, data);
}
/**
* @dev Internal function to mint an amount of a token with the given ID
* @param to The address that will own the minted token
* @param id ID of the token to be minted
* @param value Amount of the token to be minted
* @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function _mint(address to, uint256 id, uint256 value, bytes memory data) internal {
require(to != address(0), "ERC1155: mint to the zero address");
_balances[id][to] = value.add(_balances[id][to]);
emit TransferSingle(msg.sender, address(0), to, id, value);
_doSafeTransferAcceptanceCheck(msg.sender, address(0), to, id, value, data);
}
/**
* @dev Internal function to batch mint amounts of tokens with the given IDs
* @param to The address that will own the minted token
* @param ids IDs of the tokens to be minted
* @param values Amounts of the tokens to be minted
* @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function _batchMint(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal {
require(to != address(0), "ERC1155: batch mint to the zero address");
require(ids.length == values.length, "ERC1155: IDs and values must have same lengths");
for(uint i = 0; i < ids.length; i++) {
_balances[ids[i]][to] = values[i].add(_balances[ids[i]][to]);
}
emit TransferBatch(msg.sender, address(0), to, ids, values);
_doSafeBatchTransferAcceptanceCheck(msg.sender, address(0), to, ids, values, data);
}
/**
* @dev Internal function to burn an amount of a token with the given ID
* @param owner Account which owns the token to be burnt
* @param id ID of the token to be burnt
* @param value Amount of the token to be burnt
*/
function _burn(address owner, uint256 id, uint256 value) internal {
_balances[id][owner] = _balances[id][owner].sub(value);
emit TransferSingle(msg.sender, owner, address(0), id, value);
}
/**
* @dev Internal function to batch burn an amounts of tokens with the given IDs
* @param owner Account which owns the token to be burnt
* @param ids IDs of the tokens to be burnt
* @param values Amounts of the tokens to be burnt
*/
function _batchBurn(address owner, uint256[] memory ids, uint256[] memory values) internal {
require(ids.length == values.length, "ERC1155: IDs and values must have same lengths");
for(uint i = 0; i < ids.length; i++) {
_balances[ids[i]][owner] = _balances[ids[i]][owner].sub(values[i]);
}
emit TransferBatch(msg.sender, owner, address(0), ids, values);
}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
)
internal
{
if(to.isContract()) {
require(
IERC1155TokenReceiver(to).onERC1155Received(operator, from, id, value, data) ==
IERC1155TokenReceiver(to).onERC1155Received.selector,
"ERC1155: got unknown value from onERC1155Received"
);
}
}
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
)
internal
{
if(to.isContract()) {
require(
IERC1155TokenReceiver(to).onERC1155BatchReceived(operator, from, ids, values, data) == IERC1155TokenReceiver(to).onERC1155BatchReceived.selector,
"ERC1155: got unknown value from onERC1155BatchReceived"
);
}
}
}
// File: @gnosis.pm/conditional-tokens-contracts/contracts/CTHelpers.sol
pragma solidity ^0.5.1;
library CTHelpers {
/// @dev Constructs a condition ID from an oracle, a question ID, and the outcome slot count for the question.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
function getConditionId(address oracle, bytes32 questionId, uint outcomeSlotCount) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount));
}
uint constant P = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
uint constant B = 3;
function sqrt(uint x) private pure returns (uint y) {
uint p = P;
// solium-disable-next-line security/no-inline-assembly
assembly {
// add chain generated via https://crypto.stackexchange.com/q/27179/71252
// and transformed to the following program:
// x=1; y=x+x; z=y+y; z=z+z; y=y+z; x=x+y; y=y+x; z=y+y; t=z+z; t=z+t; t=t+t;
// t=t+t; z=z+t; x=x+z; z=x+x; z=z+z; y=y+z; z=y+y; z=z+z; z=z+z; z=y+z; x=x+z;
// z=x+x; z=z+z; z=z+z; z=x+z; y=y+z; x=x+y; z=x+x; z=z+z; y=y+z; z=y+y; t=z+z;
// t=t+t; t=t+t; z=z+t; x=x+z; y=y+x; z=y+y; z=z+z; z=z+z; x=x+z; z=x+x; z=z+z;
// z=x+z; z=z+z; z=z+z; z=x+z; y=y+z; z=y+y; t=z+z; t=t+t; t=z+t; t=y+t; t=t+t;
// t=t+t; t=t+t; t=t+t; z=z+t; x=x+z; z=x+x; z=x+z; y=y+z; z=y+y; z=y+z; z=z+z;
// t=z+z; t=z+t; w=t+t; w=w+w; w=w+w; w=w+w; w=w+w; t=t+w; z=z+t; x=x+z; y=y+x;
// z=y+y; x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; z=x+x; z=x+z; z=z+z; y=y+z; z=y+y;
// z=z+z; x=x+z; y=y+x; z=y+y; z=y+z; x=x+z; y=y+x; x=x+y; y=y+x; z=y+y; z=z+z;
// z=y+z; x=x+z; z=x+x; z=x+z; y=y+z; x=x+y; y=y+x; x=x+y; y=y+x; z=y+y; z=y+z;
// z=z+z; x=x+z; y=y+x; z=y+y; z=y+z; z=z+z; x=x+z; z=x+x; t=z+z; t=t+t; t=z+t;
// t=x+t; t=t+t; t=t+t; t=t+t; t=t+t; z=z+t; y=y+z; x=x+y; y=y+x; x=x+y; z=x+x;
// z=x+z; z=z+z; z=z+z; z=z+z; z=x+z; y=y+z; z=y+y; z=y+z; z=z+z; x=x+z; z=x+x;
// z=x+z; y=y+z; x=x+y; z=x+x; z=z+z; y=y+z; x=x+y; z=x+x; y=y+z; x=x+y; y=y+x;
// z=y+y; z=y+z; x=x+z; y=y+x; z=y+y; z=y+z; z=z+z; z=z+z; x=x+z; z=x+x; z=z+z;
// z=z+z; z=x+z; y=y+z; x=x+y; z=x+x; t=x+z; t=t+t; t=t+t; z=z+t; y=y+z; z=y+y;
// x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; y=y+x; z=y+y; t=y+z; z=y+t; z=z+z; z=z+z;
// z=t+z; x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; z=x+x; z=x+z; y=y+z; x=x+y; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
// x=x+x; x=x+x; x=x+x; x=x+x; res=y+x
// res == (P + 1) // 4
y := mulmod(x, x, p)
{
let z := mulmod(y, y, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
{
let t := mulmod(z, z, p)
t := mulmod(z, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
t := mulmod(z, z, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
t := mulmod(z, z, p)
t := mulmod(t, t, p)
t := mulmod(z, t, p)
t := mulmod(y, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
t := mulmod(z, z, p)
t := mulmod(z, t, p)
{
let w := mulmod(t, t, p)
w := mulmod(w, w, p)
w := mulmod(w, w, p)
w := mulmod(w, w, p)
w := mulmod(w, w, p)
t := mulmod(t, w, p)
}
z := mulmod(z, t, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(z, z, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
t := mulmod(z, z, p)
t := mulmod(t, t, p)
t := mulmod(z, t, p)
t := mulmod(x, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
z := mulmod(y, z, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
x := mulmod(x, z, p)
z := mulmod(x, x, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
t := mulmod(x, z, p)
t := mulmod(t, t, p)
t := mulmod(t, t, p)
z := mulmod(z, t, p)
y := mulmod(y, z, p)
z := mulmod(y, y, p)
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
z := mulmod(y, y, p)
t := mulmod(y, z, p)
z := mulmod(y, t, p)
z := mulmod(z, z, p)
z := mulmod(z, z, p)
z := mulmod(t, z, p)
}
x := mulmod(x, z, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
y := mulmod(y, x, p)
x := mulmod(x, y, p)
z := mulmod(x, x, p)
z := mulmod(x, z, p)
y := mulmod(y, z, p)
}
x := mulmod(x, y, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
x := mulmod(x, x, p)
y := mulmod(y, x, p)
}
}
/// @dev Constructs an outcome collection ID from a parent collection and an outcome collection.
/// @param parentCollectionId Collection ID of the parent outcome collection, or bytes32(0) if there's no parent.
/// @param conditionId Condition ID of the outcome collection to combine with the parent outcome collection.
/// @param indexSet Index set of the outcome collection to combine with the parent outcome collection.
function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint indexSet) internal view returns (bytes32) {
uint x1 = uint(keccak256(abi.encodePacked(conditionId, indexSet)));
bool odd = x1 >> 255 != 0;
uint y1;
uint yy;
do {
x1 = addmod(x1, 1, P);
yy = addmod(mulmod(x1, mulmod(x1, x1, P), P), B, P);
y1 = sqrt(yy);
} while(mulmod(y1, y1, P) != yy);
if(odd && y1 % 2 == 0 || !odd && y1 % 2 == 1)
y1 = P - y1;
uint x2 = uint(parentCollectionId);
if(x2 != 0) {
odd = x2 >> 254 != 0;
x2 = (x2 << 2) >> 2;
yy = addmod(mulmod(x2, mulmod(x2, x2, P), P), B, P);
uint y2 = sqrt(yy);
if(odd && y2 % 2 == 0 || !odd && y2 % 2 == 1)
y2 = P - y2;
require(mulmod(y2, y2, P) == yy, "invalid parent collection ID");
(bool success, bytes memory ret) = address(6).staticcall(abi.encode(x1, y1, x2, y2));
require(success, "ecadd failed");
(x1, y1) = abi.decode(ret, (uint, uint));
}
if(y1 % 2 == 1)
x1 ^= 1 << 254;
return bytes32(x1);
}
/// @dev Constructs a position ID from a collateral token and an outcome collection. These IDs are used as the ERC-1155 ID for this contract.
/// @param collateralToken Collateral token which backs the position.
/// @param collectionId ID of the outcome collection associated with this position.
function getPositionId(IERC20 collateralToken, bytes32 collectionId) internal pure returns (uint) {
return uint(keccak256(abi.encodePacked(collateralToken, collectionId)));
}
}
// File: @gnosis.pm/conditional-tokens-contracts/contracts/ConditionalTokens.sol
pragma solidity ^0.5.1;
contract ConditionalTokens is ERC1155 {
/// @dev Emitted upon the successful preparation of a condition.
/// @param conditionId The condition's ID. This ID may be derived from the other three parameters via ``keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount))``.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
event ConditionPreparation(
bytes32 indexed conditionId,
address indexed oracle,
bytes32 indexed questionId,
uint outcomeSlotCount
);
event ConditionResolution(
bytes32 indexed conditionId,
address indexed oracle,
bytes32 indexed questionId,
uint outcomeSlotCount,
uint[] payoutNumerators
);
/// @dev Emitted when a position is successfully split.
event PositionSplit(
address indexed stakeholder,
IERC20 collateralToken,
bytes32 indexed parentCollectionId,
bytes32 indexed conditionId,
uint[] partition,
uint amount
);
/// @dev Emitted when positions are successfully merged.
event PositionsMerge(
address indexed stakeholder,
IERC20 collateralToken,
bytes32 indexed parentCollectionId,
bytes32 indexed conditionId,
uint[] partition,
uint amount
);
event PayoutRedemption(
address indexed redeemer,
IERC20 indexed collateralToken,
bytes32 indexed parentCollectionId,
bytes32 conditionId,
uint[] indexSets,
uint payout
);
/// Mapping key is an condition ID. Value represents numerators of the payout vector associated with the condition. This array is initialized with a length equal to the outcome slot count. E.g. Condition with 3 outcomes [A, B, C] and two of those correct [0.5, 0.5, 0]. In Ethereum there are no decimal values, so here, 0.5 is represented by fractions like 1/2 == 0.5. That's why we need numerator and denominator values. Payout numerators are also used as a check of initialization. If the numerators array is empty (has length zero), the condition was not created/prepared. See getOutcomeSlotCount.
mapping(bytes32 => uint[]) public payoutNumerators;
/// Denominator is also used for checking if the condition has been resolved. If the denominator is non-zero, then the condition has been resolved.
mapping(bytes32 => uint) public payoutDenominator;
/// @dev This function prepares a condition by initializing a payout vector associated with the condition.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
function prepareCondition(address oracle, bytes32 questionId, uint outcomeSlotCount) external {
// Limit of 256 because we use a partition array that is a number of 256 bits.
require(outcomeSlotCount <= 256, "too many outcome slots");
require(outcomeSlotCount > 1, "there should be more than one outcome slot");
bytes32 conditionId = CTHelpers.getConditionId(oracle, questionId, outcomeSlotCount);
require(payoutNumerators[conditionId].length == 0, "condition already prepared");
payoutNumerators[conditionId] = new uint[](outcomeSlotCount);
emit ConditionPreparation(conditionId, oracle, questionId, outcomeSlotCount);
}
/// @dev Called by the oracle for reporting results of conditions. Will set the payout vector for the condition with the ID ``keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount))``, where oracle is the message sender, questionId is one of the parameters of this function, and outcomeSlotCount is the length of the payouts parameter, which contains the payoutNumerators for each outcome slot of the condition.
/// @param questionId The question ID the oracle is answering for
/// @param payouts The oracle's answer
function reportPayouts(bytes32 questionId, uint[] calldata payouts) external {
uint outcomeSlotCount = payouts.length;
require(outcomeSlotCount > 1, "there should be more than one outcome slot");
// IMPORTANT, the oracle is enforced to be the sender because it's part of the hash.
bytes32 conditionId = CTHelpers.getConditionId(msg.sender, questionId, outcomeSlotCount);
require(payoutNumerators[conditionId].length == outcomeSlotCount, "condition not prepared or found");
require(payoutDenominator[conditionId] == 0, "payout denominator already set");
uint den = 0;
for (uint i = 0; i < outcomeSlotCount; i++) {
uint num = payouts[i];
den = den.add(num);
require(payoutNumerators[conditionId][i] == 0, "payout numerator already set");
payoutNumerators[conditionId][i] = num;
}
require(den > 0, "payout is all zeroes");
payoutDenominator[conditionId] = den;
emit ConditionResolution(conditionId, msg.sender, questionId, outcomeSlotCount, payoutNumerators[conditionId]);
}
/// @dev This function splits a position. If splitting from the collateral, this contract will attempt to transfer `amount` collateral from the message sender to itself. Otherwise, this contract will burn `amount` stake held by the message sender in the position being split worth of EIP 1155 tokens. Regardless, if successful, `amount` stake will be minted in the split target positions. If any of the transfers, mints, or burns fail, the transaction will revert. The transaction will also revert if the given partition is trivial, invalid, or refers to more slots than the condition is prepared with.
/// @param collateralToken The address of the positions' backing collateral token.
/// @param parentCollectionId The ID of the outcome collections common to the position being split and the split target positions. May be null, in which only the collateral is shared.
/// @param conditionId The ID of the condition to split on.
/// @param partition An array of disjoint index sets representing a nontrivial partition of the outcome slots of the given condition. E.g. A|B and C but not A|B and B|C (is not disjoint). Each element's a number which, together with the condition, represents the outcome collection. E.g. 0b110 is A|B, 0b010 is B, etc.
/// @param amount The amount of collateral or stake to split.
function splitPosition(
IERC20 collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint[] calldata partition,
uint amount
) external {
require(partition.length > 1, "got empty or singleton partition");
uint outcomeSlotCount = payoutNumerators[conditionId].length;
require(outcomeSlotCount > 0, "condition not prepared yet");
// For a condition with 4 outcomes fullIndexSet's 0b1111; for 5 it's 0b11111...
uint fullIndexSet = (1 << outcomeSlotCount) - 1;
// freeIndexSet starts as the full collection
uint freeIndexSet = fullIndexSet;
// This loop checks that all condition sets are disjoint (the same outcome is not part of more than 1 set)
uint[] memory positionIds = new uint[](partition.length);
uint[] memory amounts = new uint[](partition.length);
for (uint i = 0; i < partition.length; i++) {
uint indexSet = partition[i];
require(indexSet > 0 && indexSet < fullIndexSet, "got invalid index set");
require((indexSet & freeIndexSet) == indexSet, "partition not disjoint");
freeIndexSet ^= indexSet;
positionIds[i] = CTHelpers.getPositionId(collateralToken, CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet));
amounts[i] = amount;
}
if (freeIndexSet == 0) {
// Partitioning the full set of outcomes for the condition in this branch
if (parentCollectionId == bytes32(0)) {
require(collateralToken.transferFrom(msg.sender, address(this), amount), "could not receive collateral tokens");
} else {
_burn(
msg.sender,
CTHelpers.getPositionId(collateralToken, parentCollectionId),
amount
);
}
} else {
// Partitioning a subset of outcomes for the condition in this branch.
// For example, for a condition with three outcomes A, B, and C, this branch
// allows the splitting of a position $:(A|C) to positions $:(A) and $:(C).
_burn(
msg.sender,
CTHelpers.getPositionId(collateralToken,
CTHelpers.getCollectionId(parentCollectionId, conditionId, fullIndexSet ^ freeIndexSet)),
amount
);
}
_batchMint(
msg.sender,
// position ID is the ERC 1155 token ID
positionIds,
amounts,
""
);
emit PositionSplit(msg.sender, collateralToken, parentCollectionId, conditionId, partition, amount);
}
function mergePositions(
IERC20 collateralToken,
bytes32 parentCollectionId,
bytes32 conditionId,
uint[] calldata partition,
uint amount
) external {
require(partition.length > 1, "got empty or singleton partition");
uint outcomeSlotCount = payoutNumerators[conditionId].length;
require(outcomeSlotCount > 0, "condition not prepared yet");
uint fullIndexSet = (1 << outcomeSlotCount) - 1;
uint freeIndexSet = fullIndexSet;
uint[] memory positionIds = new uint[](partition.length);
uint[] memory amounts = new uint[](partition.length);
for (uint i = 0; i < partition.length; i++) {
uint indexSet = partition[i];
require(indexSet > 0 && indexSet < fullIndexSet, "got invalid index set");
require((indexSet & freeIndexSet) == indexSet, "partition not disjoint");
freeIndexSet ^= indexSet;
positionIds[i] = CTHelpers.getPositionId(collateralToken, CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet));
amounts[i] = amount;
}
_batchBurn(
msg.sender,
positionIds,
amounts
);
if (freeIndexSet == 0) {
if (parentCollectionId == bytes32(0)) {
require(collateralToken.transfer(msg.sender, amount), "could not send collateral tokens");
} else {
_mint(
msg.sender,
CTHelpers.getPositionId(collateralToken, parentCollectionId),
amount,
""
);
}
} else {
_mint(
msg.sender,
CTHelpers.getPositionId(collateralToken,
CTHelpers.getCollectionId(parentCollectionId, conditionId, fullIndexSet ^ freeIndexSet)),
amount,
""
);
}
emit PositionsMerge(msg.sender, collateralToken, parentCollectionId, conditionId, partition, amount);
}
function redeemPositions(IERC20 collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] calldata indexSets) external {
uint den = payoutDenominator[conditionId];
require(den > 0, "result for condition not received yet");
uint outcomeSlotCount = payoutNumerators[conditionId].length;
require(outcomeSlotCount > 0, "condition not prepared yet");
uint totalPayout = 0;
uint fullIndexSet = (1 << outcomeSlotCount) - 1;
for (uint i = 0; i < indexSets.length; i++) {
uint indexSet = indexSets[i];
require(indexSet > 0 && indexSet < fullIndexSet, "got invalid index set");
uint positionId = CTHelpers.getPositionId(collateralToken,
CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet));
uint payoutNumerator = 0;
for (uint j = 0; j < outcomeSlotCount; j++) {
if (indexSet & (1 << j) != 0) {
payoutNumerator = payoutNumerator.add(payoutNumerators[conditionId][j]);
}
}
uint payoutStake = balanceOf(msg.sender, positionId);
if (payoutStake > 0) {
totalPayout = totalPayout.add(payoutStake.mul(payoutNumerator).div(den));
_burn(msg.sender, positionId, payoutStake);
}
}
if (totalPayout > 0) {
if (parentCollectionId == bytes32(0)) {
require(collateralToken.transfer(msg.sender, totalPayout), "could not transfer payout to message sender");
} else {
_mint(msg.sender, CTHelpers.getPositionId(collateralToken, parentCollectionId), totalPayout, "");
}
}
emit PayoutRedemption(msg.sender, collateralToken, parentCollectionId, conditionId, indexSets, totalPayout);
}
/// @dev Gets the outcome slot count of a condition.
/// @param conditionId ID of the condition.
/// @return Number of outcome slots associated with a condition, or zero if condition has not been prepared yet.
function getOutcomeSlotCount(bytes32 conditionId) external view returns (uint) {
return payoutNumerators[conditionId].length;
}
/// @dev Constructs a condition ID from an oracle, a question ID, and the outcome slot count for the question.
/// @param oracle The account assigned to report the result for the prepared condition.
/// @param questionId An identifier for the question to be answered by the oracle.
/// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
function getConditionId(address oracle, bytes32 questionId, uint outcomeSlotCount) external pure returns (bytes32) {
return CTHelpers.getConditionId(oracle, questionId, outcomeSlotCount);
}
/// @dev Constructs an outcome collection ID from a parent collection and an outcome collection.
/// @param parentCollectionId Collection ID of the parent outcome collection, or bytes32(0) if there's no parent.
/// @param conditionId Condition ID of the outcome collection to combine with the parent outcome collection.
/// @param indexSet Index set of the outcome collection to combine with the parent outcome collection.
function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint indexSet) external view returns (bytes32) {
return CTHelpers.getCollectionId(parentCollectionId, conditionId, indexSet);
}
/// @dev Constructs a position ID from a collateral token and an outcome collection. These IDs are used as the ERC-1155 ID for this contract.
/// @param collateralToken Collateral token which backs the position.
/// @param collectionId ID of the outcome collection associated with this position.
function getPositionId(IERC20 collateralToken, bytes32 collectionId) external pure returns (uint) {
return CTHelpers.getPositionId(collateralToken, collectionId);
}
}
// File: @gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/ERC1155TokenReceiver.sol
pragma solidity ^0.5.0;
contract ERC1155TokenReceiver is ERC165, IERC1155TokenReceiver {
constructor() public {
_registerInterface(
ERC1155TokenReceiver(0).onERC1155Received.selector ^
ERC1155TokenReceiver(0).onERC1155BatchReceived.selector
);
}
}
// File: contracts/ERC20.sol
// This is a backported hooked OpenZeppelin ERC 20 implementation
pragma solidity ^0.5.0;
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount);
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of `from`'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of `from`'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal { }
}
// File: contracts/FixedProductMarketMaker.sol
pragma solidity ^0.5.1;
library CeilDiv {
// calculates ceil(x/y)
function ceildiv(uint x, uint y) internal pure returns (uint) {
if(x > 0) return ((x - 1) / y) + 1;
return x / y;
}
}
contract FixedProductMarketMaker is ERC20, ERC1155TokenReceiver {
event FPMMFundingAdded(
address indexed funder,
uint[] amountsAdded,
uint sharesMinted
);
event FPMMFundingRemoved(
address indexed funder,
uint[] amountsRemoved,
uint collateralRemovedFromFeePool,
uint sharesBurnt
);
event FPMMBuy(
address indexed buyer,
uint investmentAmount,
uint feeAmount,
uint indexed outcomeIndex,
uint outcomeTokensBought
);
event FPMMSell(
address indexed seller,
uint returnAmount,
uint feeAmount,
uint indexed outcomeIndex,
uint outcomeTokensSold
);
using SafeMath for uint;
using CeilDiv for uint;
uint constant ONE = 10**18;
ConditionalTokens public conditionalTokens;
IERC20 public collateralToken;
bytes32[] public conditionIds;
uint public fee;
uint internal feePoolWeight;
uint[] outcomeSlotCounts;
bytes32[][] collectionIds;
uint[] positionIds;
mapping (address => uint256) withdrawnFees;
uint internal totalWithdrawnFees;
function getPoolBalances() private view returns (uint[] memory) {
address[] memory thises = new address[](positionIds.length);
for(uint i = 0; i < positionIds.length; i++) {
thises[i] = address(this);
}
return conditionalTokens.balanceOfBatch(thises, positionIds);
}
function generateBasicPartition(uint outcomeSlotCount)
private
pure
returns (uint[] memory partition)
{
partition = new uint[](outcomeSlotCount);
for(uint i = 0; i < outcomeSlotCount; i++) {
partition[i] = 1 << i;
}
}
function splitPositionThroughAllConditions(uint amount)
private
{
for(uint i = conditionIds.length - 1; int(i) >= 0; i--) {
uint[] memory partition = generateBasicPartition(outcomeSlotCounts[i]);
for(uint j = 0; j < collectionIds[i].length; j++) {
conditionalTokens.splitPosition(collateralToken, collectionIds[i][j], conditionIds[i], partition, amount);
}
}
}
function mergePositionsThroughAllConditions(uint amount)
private
{
for(uint i = 0; i < conditionIds.length; i++) {
uint[] memory partition = generateBasicPartition(outcomeSlotCounts[i]);
for(uint j = 0; j < collectionIds[i].length; j++) {
conditionalTokens.mergePositions(collateralToken, collectionIds[i][j], conditionIds[i], partition, amount);
}
}
}
function collectedFees() external view returns (uint) {
return feePoolWeight.sub(totalWithdrawnFees);
}
function feesWithdrawableBy(address account) public view returns (uint) {
uint rawAmount = feePoolWeight.mul(balanceOf(account)) / totalSupply();
return rawAmount.sub(withdrawnFees[account]);
}
function withdrawFees(address account) public {
uint rawAmount = feePoolWeight.mul(balanceOf(account)) / totalSupply();
uint withdrawableAmount = rawAmount.sub(withdrawnFees[account]);
if(withdrawableAmount > 0){
withdrawnFees[account] = rawAmount;
totalWithdrawnFees = totalWithdrawnFees.add(withdrawableAmount);
require(collateralToken.transfer(account, withdrawableAmount), "withdrawal transfer failed");
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal {
if (from != address(0)) {
withdrawFees(from);
}
uint totalSupply = totalSupply();
uint withdrawnFeesTransfer = totalSupply == 0 ?
amount :
feePoolWeight.mul(amount) / totalSupply;
if (from != address(0)) {
withdrawnFees[from] = withdrawnFees[from].sub(withdrawnFeesTransfer);
totalWithdrawnFees = totalWithdrawnFees.sub(withdrawnFeesTransfer);
} else {
feePoolWeight = feePoolWeight.add(withdrawnFeesTransfer);
}
if (to != address(0)) {
withdrawnFees[to] = withdrawnFees[to].add(withdrawnFeesTransfer);
totalWithdrawnFees = totalWithdrawnFees.add(withdrawnFeesTransfer);
} else {
feePoolWeight = feePoolWeight.sub(withdrawnFeesTransfer);
}
}
function addFunding(uint addedFunds, uint[] calldata distributionHint)
external
{
require(addedFunds > 0, "funding must be non-zero");
uint[] memory sendBackAmounts = new uint[](positionIds.length);
uint poolShareSupply = totalSupply();
uint mintAmount;
if(poolShareSupply > 0) {
require(distributionHint.length == 0, "cannot use distribution hint after initial funding");
uint[] memory poolBalances = getPoolBalances();
uint poolWeight = 0;
for(uint i = 0; i < poolBalances.length; i++) {
uint balance = poolBalances[i];
if(poolWeight < balance)
poolWeight = balance;
}
for(uint i = 0; i < poolBalances.length; i++) {
uint remaining = addedFunds.mul(poolBalances[i]) / poolWeight;
sendBackAmounts[i] = addedFunds.sub(remaining);
}
mintAmount = addedFunds.mul(poolShareSupply) / poolWeight;
} else {
if(distributionHint.length > 0) {
require(distributionHint.length == positionIds.length, "hint length off");
uint maxHint = 0;
for(uint i = 0; i < distributionHint.length; i++) {
uint hint = distributionHint[i];
if(maxHint < hint)
maxHint = hint;
}
for(uint i = 0; i < distributionHint.length; i++) {
uint remaining = addedFunds.mul(distributionHint[i]) / maxHint;
require(remaining > 0, "must hint a valid distribution");
sendBackAmounts[i] = addedFunds.sub(remaining);
}
}
mintAmount = addedFunds;
}
require(collateralToken.transferFrom(msg.sender, address(this), addedFunds), "funding transfer failed");
require(collateralToken.approve(address(conditionalTokens), addedFunds), "approval for splits failed");
splitPositionThroughAllConditions(addedFunds);
_mint(msg.sender, mintAmount);
conditionalTokens.safeBatchTransferFrom(address(this), msg.sender, positionIds, sendBackAmounts, "");
// transform sendBackAmounts to array of amounts added
for (uint i = 0; i < sendBackAmounts.length; i++) {
sendBackAmounts[i] = addedFunds.sub(sendBackAmounts[i]);
}
emit FPMMFundingAdded(msg.sender, sendBackAmounts, mintAmount);
}
function removeFunding(uint sharesToBurn)
external
{
uint[] memory poolBalances = getPoolBalances();
uint[] memory sendAmounts = new uint[](poolBalances.length);
uint poolShareSupply = totalSupply();
for(uint i = 0; i < poolBalances.length; i++) {
sendAmounts[i] = poolBalances[i].mul(sharesToBurn) / poolShareSupply;
}
uint collateralRemovedFromFeePool = collateralToken.balanceOf(address(this));
_burn(msg.sender, sharesToBurn);
collateralRemovedFromFeePool = collateralRemovedFromFeePool.sub(
collateralToken.balanceOf(address(this))
);
conditionalTokens.safeBatchTransferFrom(address(this), msg.sender, positionIds, sendAmounts, "");
emit FPMMFundingRemoved(msg.sender, sendAmounts, collateralRemovedFromFeePool, sharesToBurn);
}
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
)
external
returns (bytes4)
{
if (operator == address(this)) {
return this.onERC1155Received.selector;
}
return 0x0;
}
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
)
external
returns (bytes4)
{
if (operator == address(this) && from == address(0)) {
return this.onERC1155BatchReceived.selector;
}
return 0x0;
}
function calcBuyAmount(uint investmentAmount, uint outcomeIndex) public view returns (uint) {
require(outcomeIndex < positionIds.length, "invalid outcome index");
uint[] memory poolBalances = getPoolBalances();
uint investmentAmountMinusFees = investmentAmount.sub(investmentAmount.mul(fee) / ONE);
uint buyTokenPoolBalance = poolBalances[outcomeIndex];
uint endingOutcomeBalance = buyTokenPoolBalance.mul(ONE);
for(uint i = 0; i < poolBalances.length; i++) {
if(i != outcomeIndex) {
uint poolBalance = poolBalances[i];
endingOutcomeBalance = endingOutcomeBalance.mul(poolBalance).ceildiv(
poolBalance.add(investmentAmountMinusFees)
);
}
}
require(endingOutcomeBalance > 0, "must have non-zero balances");
return buyTokenPoolBalance.add(investmentAmountMinusFees).sub(endingOutcomeBalance.ceildiv(ONE));
}
function calcSellAmount(uint returnAmount, uint outcomeIndex) public view returns (uint outcomeTokenSellAmount) {
require(outcomeIndex < positionIds.length, "invalid outcome index");
uint[] memory poolBalances = getPoolBalances();
uint returnAmountPlusFees = returnAmount.mul(ONE) / ONE.sub(fee);
uint sellTokenPoolBalance = poolBalances[outcomeIndex];
uint endingOutcomeBalance = sellTokenPoolBalance.mul(ONE);
for(uint i = 0; i < poolBalances.length; i++) {
if(i != outcomeIndex) {
uint poolBalance = poolBalances[i];
endingOutcomeBalance = endingOutcomeBalance.mul(poolBalance).ceildiv(
poolBalance.sub(returnAmountPlusFees)
);
}
}
require(endingOutcomeBalance > 0, "must have non-zero balances");
return returnAmountPlusFees.add(endingOutcomeBalance.ceildiv(ONE)).sub(sellTokenPoolBalance);
}
function buy(uint investmentAmount, uint outcomeIndex, uint minOutcomeTokensToBuy) external {
uint outcomeTokensToBuy = calcBuyAmount(investmentAmount, outcomeIndex);
require(outcomeTokensToBuy >= minOutcomeTokensToBuy, "minimum buy amount not reached");
require(collateralToken.transferFrom(msg.sender, address(this), investmentAmount), "cost transfer failed");
uint feeAmount = investmentAmount.mul(fee) / ONE;
feePoolWeight = feePoolWeight.add(feeAmount);
uint investmentAmountMinusFees = investmentAmount.sub(feeAmount);
require(collateralToken.approve(address(conditionalTokens), investmentAmountMinusFees), "approval for splits failed");
splitPositionThroughAllConditions(investmentAmountMinusFees);
conditionalTokens.safeTransferFrom(address(this), msg.sender, positionIds[outcomeIndex], outcomeTokensToBuy, "");
emit FPMMBuy(msg.sender, investmentAmount, feeAmount, outcomeIndex, outcomeTokensToBuy);
}
function sell(uint returnAmount, uint outcomeIndex, uint maxOutcomeTokensToSell) external {
uint outcomeTokensToSell = calcSellAmount(returnAmount, outcomeIndex);
require(outcomeTokensToSell <= maxOutcomeTokensToSell, "maximum sell amount exceeded");
conditionalTokens.safeTransferFrom(msg.sender, address(this), positionIds[outcomeIndex], outcomeTokensToSell, "");
uint feeAmount = returnAmount.mul(fee) / (ONE.sub(fee));
feePoolWeight = feePoolWeight.add(feeAmount);
uint returnAmountPlusFees = returnAmount.add(feeAmount);
mergePositionsThroughAllConditions(returnAmountPlusFees);
require(collateralToken.transfer(msg.sender, returnAmount), "return transfer failed");
emit FPMMSell(msg.sender, returnAmount, feeAmount, outcomeIndex, outcomeTokensToSell);
}
}
// for proxying purposes
contract FixedProductMarketMakerData {
mapping (address => uint256) internal _balances;
mapping (address => mapping (address => uint256)) internal _allowances;
uint256 internal _totalSupply;
bytes4 internal constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
mapping(bytes4 => bool) internal _supportedInterfaces;
event FPMMFundingAdded(
address indexed funder,
uint[] amountsAdded,
uint sharesMinted
);
event FPMMFundingRemoved(
address indexed funder,
uint[] amountsRemoved,
uint collateralRemovedFromFeePool,
uint sharesBurnt
);
event FPMMBuy(
address indexed buyer,
uint investmentAmount,
uint feeAmount,
uint indexed outcomeIndex,
uint outcomeTokensBought
);
event FPMMSell(
address indexed seller,
uint returnAmount,
uint feeAmount,
uint indexed outcomeIndex,
uint outcomeTokensSold
);
ConditionalTokens internal conditionalTokens;
IERC20 internal collateralToken;
bytes32[] internal conditionIds;
uint internal fee;
uint internal feePoolWeight;
uint[] internal outcomeSlotCounts;
bytes32[][] internal collectionIds;
uint[] internal positionIds;
mapping (address => uint256) internal withdrawnFees;
uint internal totalWithdrawnFees;
}