Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 4 internal transactions
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
2041113 | 44 days ago | Contract Creation | 0 S | |||
2041113 | 44 days ago | Contract Creation | 0 S | |||
2041108 | 44 days ago | Contract Creation | 0 S | |||
2041108 | 44 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
TrieFactory
Compiler Version
v0.8.28+commit.7893614a
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 // Central Limit Order Book (CLOB) exchange // (c) Long Gamma Labs, 2023. pragma solidity ^0.8.26; import {Trie} from "./Trie.sol"; import {ITrieFactory} from "./ITrieFactory.sol"; /// @title Trie Factory for Protocol /// @dev Factory for creating instances of Trie contract TrieFactory is ITrieFactory { /// @dev Creates a new Trie /// @param lob The address to pass to the Trie constructor /// @return The address of the newly created Trie function createTrie(address lob) external returns (address) { Trie trie = new Trie(lob); return address(trie); } }
// SPDX-License-Identifier: BUSL-1.1 // Central Limit Order Book (CLOB) exchange // (c) Long Gamma Labs, 2023. pragma solidity ^0.8.26; import {Errors} from "./Errors.sol"; import {ITrie} from "./ITrie.sol"; struct Node { uint64 left; uint64 right; uint64 total_shares_value_ptr; } struct Leaf { uint64 trader_id; uint128 remain_shares; uint64 initial_shares_value_ptr; } struct ExecutionResult { uint128 executed_shares; uint128 executed_value; uint128 total_shares; uint128 total_value; uint64 right_child; uint64 xor_map; } /// @title Trie data structure for Protocol /// @dev This contract implements a trie for efficient order management in an on-chain limit order book. contract Trie is ITrie { address immutable lob_address; // Since we have a prefix tree, we can immediately get all the keys in the rightmost branch. // Each right key represents a part of best_offer, the length of which is determined // by the non-zero bit in rightmost_map. That is, the number of non-zero bits // in rightmost_map equals the number of nodes in the rightmost branch. uint64 public best_offer = 0; uint64 public rightmost_map = 0x0; // The rightmost branch is unique: it only stores the total_shares and total_amount of the left child, // so deleting or adding new nodes can start directly from the corresponding node on the rightmost branch. uint64 highest_allocated_pointer; uint64 last_free_pointer; mapping (uint64 => Leaf) leaves; mapping (uint64 => Node) nodes; mapping (uint64 => uint256) arena; modifier onlyLOBPermission() { require(msg.sender == lob_address, Errors.Forbidden()); _; } constructor(address _lob_address) { lob_address = _lob_address; } /// @dev Adds an order to the order book. /// @param trader_id The ID of the trader placing the order, must be be greater than 0. /// @param order_id The ID of the order being placed, must be unique and be pointer to leaf. /// @param shares The total number of shares in the order, must be greater than 0. /// @param value The total value of the order, must be greater than 0. /// All restrictions on parameters must be satisfied by the logic of the LOB contract function addOrder(uint64 trader_id, uint64 order_id, uint128 shares, uint128 value) virtual public onlyLOBPermission { uint64 ptr = allocateMemory(); saveSharesValueToArena(ptr, shares, value); leaves[order_id] = Leaf({ trader_id: trader_id, remain_shares: shares, initial_shares_value_ptr: ptr }); uint256 map = rightmost_map; if (map == 0x0) { // This means the trie is empty. best_offer = order_id; rightmost_map = 0x1; return; } // We climb the rightmost branch until we go through it all, which means we need to add a new root, // or to a node that matches our added order_id. (uint64 node_id, bool success) = findRightmostMatchingNode(order_id); if (success) { // We found the node that matches order_id. addOrderToNodes(order_id, node_id, shares, value); } else { // We have reached the root of the tree, we need to add a new node, which will become the new root // of the tree. New node`s descendant will be new order_id leaf. // note: node_id here is the root node_id addOrderAndUpdateRoot(order_id, node_id, shares, value); } } /// @dev Removes an order with a specific order_id associated with a trader identified by order_trader_id. /// @param order_id The ID of the order to be removed. /// @param order_trader_id The ID of the trader associated with the order. /// Only needed for verification purposes. /// @return total_shares The total number of shares for the order. /// @return remain_shares The remaining number of shares for the order. function removeOrder(uint64 order_id, uint64 order_trader_id) virtual public onlyLOBPermission returns (uint128 total_shares, uint128 remain_shares) { Leaf memory leaf = leaves[order_id]; require(leaf.trader_id != 0x0, Errors.EmptyOrderError()); // An important validation check for the provided address. It prevents unauthorized order removal. require(leaf.trader_id == order_trader_id, Errors.WrongOwner()); uint128 total_value; (total_shares, total_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); remain_shares = leaf.remain_shares; uint128 remain_value = uint128(uint256(remain_shares) * uint256(total_value) / uint256(total_shares)); // To prevent double claims. delete leaves[order_id]; freeMemory(leaf.initial_shares_value_ptr); uint64 local_best_offer = best_offer; if (order_id > local_best_offer) { return (total_shares, 0); } if (order_id == local_best_offer) { removeBestOffer(); return (total_shares, remain_shares); } (uint64 node_id, bool success) = findRightmostMatchingNode(order_id); if (!success) { return (total_shares, 0); } bool found = removeOrderFromNodes(order_id, node_id, remain_shares, remain_value); if (found) { return (total_shares, remain_shares); } else { return (total_shares, 0); } } /// @dev Claims executed shares for an order with a specific order_id associated with a trader identified /// by order_trader_id. This operation does not remove the order from the order book. /// @param order_id The ID of the order for which executed shares are being claimed. /// @param order_trader_id The ID of the trader associated with the order. /// Only needed for verification purposes. /// @return executed_shares The number of executed shares for the order. /// @return remain_shares The remaining number of shares for the order after execution. function claimExecuted(uint64 order_id, uint64 order_trader_id) virtual public onlyLOBPermission returns (uint128 executed_shares, uint128 remain_shares) { Leaf memory leaf = leaves[order_id]; if (leaf.trader_id == 0x0) { return (0, 0); } // An important validation check for the provided address. It prevents unauthorized order claim. require(leaf.trader_id == order_trader_id, Errors.WrongOwner()); remain_shares = leaf.remain_shares; (uint128 total_shares, uint128 total_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); uint64 local_best_offer = best_offer; if (order_id > local_best_offer) { remain_shares = 0; } else { (uint64 node_id, bool success) = findRightmostMatchingNode(order_id); if (!success || !isOrderInTree(node_id, order_id)) { remain_shares = 0; } } executed_shares = total_shares - remain_shares; if (executed_shares == 0) { return (0, remain_shares); } if (remain_shares == 0) { delete leaves[order_id]; freeMemory(leaf.initial_shares_value_ptr); } else { uint128 remain_value = uint128( uint256(remain_shares) * uint256(total_value) / uint256(total_shares) ); saveSharesValueToArena( leaf.initial_shares_value_ptr, remain_shares, remain_value ); } } /// @dev Retrieves information about an order based on the given order_id. /// @param order_id The ID of the order to retrieve information for. /// @return total_shares The total number of shares for the order. /// @return remain_shares The remaining number of shares for the order. /// Unlike removeOrder, it does not require the existence of an order and works even on claimed and canceled /// orders, returning zeros. function getOrderInfo(uint64 order_id) public view returns (uint128 total_shares, uint128 remain_shares) { Leaf memory leaf = leaves[order_id]; if (leaf.trader_id == 0x0) { return (0, 0); } remain_shares = leaf.remain_shares; (total_shares, ) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); // works correctly in case of empty trie too: best_offer = 0x0 if (order_id > best_offer) { return (total_shares, 0); } (uint64 node_id, bool success) = findRightmostMatchingNode(order_id); if (!success) { return (total_shares, 0); } if (isOrderInTree(node_id, order_id)) { return (total_shares, remain_shares); } else { return (total_shares, 0); } } /// @dev Executes matching orders using a "right" approach, striving to fulfill orders /// as much as possible towards the right side. /// @param order_id The price identifier of the order, not associated with a real order ID. /// @param order_total_shares The total number of shares for the order being executed. function executeRight(uint64 order_id, uint128 order_total_shares) virtual public onlyLOBPermission returns (uint128 executed_shares, uint128 executed_value) { // Conceptually the most complex function in the contract. // Since we have to rebuild rightmost branch. uint64 local_best_offer = best_offer; // Works correctly in case of empty trie too: best_offer = 0x0 if (order_id > local_best_offer) { return (0, 0); } // Here we climb up the rightmost branch, similar to how it happens in findRightmostMatchingNode. // But, unfortunately, here we can no longer limit yourself to just node_id, and we have to read the node // completely. This happens because node_id contains information only about the price of orders, // but not about volumes. // Starting with rightmost leaf. Leaf memory leaf = leaves[local_best_offer]; (uint128 initial_shares, uint128 initial_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); uint128 remain_shares = leaf.remain_shares; if (order_total_shares < remain_shares) { executed_shares = order_total_shares; executed_value = uint128(uint256(executed_shares) * uint256(initial_value) / uint256(initial_shares)); // initial_value / initial_shares is exact price unchecked { leaf.remain_shares -= executed_shares; } leaves[local_best_offer] = leaf; return (executed_shares, executed_value); } // Full execution of rightmost leaf. executed_shares = remain_shares; executed_value = uint128(uint256(remain_shares) * uint256(initial_value) / uint256(initial_shares)); uint64 map = rightmost_map; (uint64 node_id, uint64 k) = calcNextRightMostNodeId(local_best_offer, map); uint64 xor_map = 0x1; // Now climbing on nodes. while (node_id != 0x0) { // We ascend along the rightmost branch from the bottom and attempt to execute the entire subtree at once. // Initially, we try to execute the order - the rightmost descendant. // Then, at each step, we essentially try to execute the entire subtree with the top at the left child // of the current node on the rightmost branch. // Since we have a prefix tree, we can guarantee that the keys of all descendants // of any node are not less than the key of the node itself. To improve this estimation, we can take // not the node_id of the node itself, but the node_id of its left child. xor_map ^= k; Node memory node = nodes[node_id]; uint64 ptr = node.total_shares_value_ptr; (uint128 node_total_shares, uint128 node_total_value) = loadSharesValueFromArena(ptr); uint64 price_key = extractKeyFrom(node.left); // we can fully execute subtree if if (((order_id ^ 0x1) <= price_key) // all its leaves has higher or equal price than our order's price && // (we are using price_key as lower bound) and (order_total_shares >= (node_total_shares + executed_shares)) // it has less or equal shares than our order's ) { // full execution executed_shares += node_total_shares; executed_value += node_total_value; eraseNode(node_id, ptr); } else { ExecutionResult memory execution_result = executeRightRec( order_id, node.left, (order_total_shares - executed_shares) ); executed_shares += execution_result.executed_shares; executed_value += execution_result.executed_value; xor_map ^= execution_result.xor_map; if (execution_result.right_child != 0x0) { // we are partially executed, dont need to execute anymore (node_id, ) = calcNextRightMostNodeId(node_id, map); if (node_id != 0x0) { nodes[node_id].right = execution_result.right_child; } break; } } (node_id, k) = calcNextRightMostNodeId(node_id, map); } if (xor_map != 0x0) { // we have to change the rightmost map rightmost_map ^= xor_map; if (rightmost_map == 0x0) { best_offer = 0x0; } } } /// @dev Preview the execution of an order on the right. /// @param order_id The ID of the order to be executed. /// @param max_shares The maximum number of shares to execute. /// @param max_value The maximum value of shares to execute. /// @return executed_shares The number of shares executed. /// @return executed_value The value of the executed shares. function previewExecuteRight(uint64 order_id, uint128 max_shares, uint128 max_value) public view returns (uint128 executed_shares, uint128 executed_value) { uint64 local_best_offer = best_offer; if (order_id > local_best_offer || max_shares == 0 || max_value == 0) { return (0, 0); } uint64 node_id = local_best_offer; uint64 k = 0x1; uint64 path_map = rightmost_map ^ 0x1; // This is a depth-first search implementation. Same idea in assembleOrderbookFromOrders. while (true) { if ((node_id & 0x1) == 0x1) { // leaf case (uint128 leaf_executed_shares, uint128 leaf_executed_value, bool full) = previewExecuteLeaf( order_id, node_id, max_shares - executed_shares, max_value - executed_value ); executed_shares += leaf_executed_shares; executed_value += leaf_executed_value; if (!full) { break; } } else { // node case (uint128 node_shares, uint128 node_value) = loadSharesValueFromArena( nodes[node_id].total_shares_value_ptr ); uint64 price_key = extractKeyFrom(nodes[node_id].left); // check if we can execute this node fully if (((order_id ^ 0x1) > price_key) || ((max_shares - executed_shares) < node_shares) || ((max_value - executed_value) < node_value) ) { // we cant unchecked { k = ~(node_id - 1) & node_id; } path_map ^= k; node_id = nodes[node_id].right; continue; } else { // we can executed_shares += node_shares; executed_value += node_value; } } (node_id, k) = calcNextRightMostNodeId(node_id, path_map); if (node_id == 0x0) { break; } path_map ^= k; node_id = nodes[node_id].left; } } /// @dev Assembles an order book from the orders in the trie. /// @param max_price_levels The maximum number of price levels to include in the order book. /// @return array_price_ids An array of price IDs for each level in the order book. /// @return array_shares An array of the total shares for each level in the order book. function assembleOrderbookFromOrders(uint24 max_price_levels) external view returns (uint24[] memory array_price_ids, uint128[] memory array_shares) { uint64 local_best_offer = best_offer; if (local_best_offer == 0x0 || max_price_levels == 0) { array_price_ids = new uint24[](0); array_shares = new uint128[](0); return (array_price_ids, array_shares); } array_price_ids = new uint24[](max_price_levels); array_shares = new uint128[](max_price_levels); array_price_ids[0] = uint24(local_best_offer >> 40); uint24 price_level = 0; uint64 node_id = local_best_offer; uint64 k = 0x1; uint64 path_map = rightmost_map ^ 0x1; // This is a depth-first search implementation. However, we don't start from the root, but // from the far right element, as if we had already descended here. while (true) { if ((node_id & type(uint40).max) != 0x0) { uint24 c_price = uint24(node_id >> 40); if (c_price != array_price_ids[price_level]) { ++price_level; if (price_level == max_price_levels) { return (array_price_ids, array_shares); } array_price_ids[price_level] = c_price; } (uint128 total_shares, ) = extractSharesValueForNode(node_id); array_shares[price_level] += total_shares; (node_id, k) = calcNextRightMostNodeId(node_id, path_map); if (node_id == 0x0) { break; } path_map ^= k; node_id = nodes[node_id].left; // this is why it works for rightmost branch nodes too } else { unchecked { k = ~(node_id - 1) & node_id; } path_map ^= k; node_id = nodes[node_id].right; } } uint24 actual_length = price_level + 1; if (actual_length < max_price_levels) { // Cropping the returned arrays to their actual length. uint24[] memory cropped_array_price_ids = new uint24[](actual_length); uint128[] memory cropped_array_shares = new uint128[](actual_length); for(uint24 i = 0; i < actual_length; ++i) { cropped_array_price_ids[i] = array_price_ids[i]; cropped_array_shares[i] = array_shares[i]; } return (cropped_array_price_ids, cropped_array_shares); } } /// @dev Finds the rightmost node that matches the given order_id. /// @param order_id The ID of the order to find the matching node for. /// @return node_id The ID of the found node or root. /// @return success Indicates whether the search was successful. /// This algo finds the lowest node in branch that meets the given criteria. function findRightmostMatchingNode(uint64 order_id) internal view returns (uint64 node_id, bool success) { uint256 map = rightmost_map; uint64 local_best_offer = best_offer; // We climb the rightmost branch until we go through it all. uint256 mask = ~uint256(0x1); while (true) { if (((local_best_offer ^ order_id) & mask) == 0x0) { // We found the node that matches order_id. uint256 k = (~mask & (mask >> 1)); // Extracts the bit that determines the length. // Shifting works correctly for the case of 64 bits. node_id = uint64((local_best_offer & mask) ^ k); return (node_id, true); } map = map & mask; if (map == 0x0) { // We have reached the root of the tree, uint256 k = (~mask & (mask >> 1)); // Extracts the bit that determines the length. // Shifting works correctly for the case of 64 bits. uint64 root_node_id = uint64((local_best_offer & mask) ^ k); return (root_node_id, false); } unchecked { // we just checked that map != 0 mask = ~(map ^ (map - 1)); } } } /// @dev Normalizes the branch of the tree up to the specified stop node. /// @param stop_node_id The ID of the node where normalization stops, must exist. /// @return extra_total_shares The total number of extra shares added during normalization. /// @return extra_total_value The total value associated with the extra shares added during normalization. /// @return new_rightmost_map The new rightmost map after normalization. function normalizeBranch(uint64 stop_node_id) internal returns (uint128 extra_total_shares, uint128 extra_total_value, uint64 new_rightmost_map) { uint256 map = rightmost_map; uint64 local_best_offer = best_offer; // loading leaf data Leaf memory leaf = leaves[local_best_offer]; (uint128 initial_shares, uint128 initial_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); extra_total_shares = leaf.remain_shares; extra_total_value = uint128(uint256(extra_total_shares) * uint256(initial_value) / uint256(initial_shares)); map ^= 0x1; if (stop_node_id == local_best_offer) { return (extra_total_shares, extra_total_value, uint64(map)); } uint256 mask; unchecked { // we are guaranteed to have a stop order higher mask = ~(map ^ (map - 1)); } while (true) { uint256 k = (~mask & (mask >> 1)); // Shifting works correctly for the case of 64 bits. map ^= k; uint64 node_id = uint64((local_best_offer & mask) ^ k); uint64 node_ptr = nodes[node_id].total_shares_value_ptr; (uint128 node_total_shares, uint128 node_total_value) = loadSharesValueFromArena(node_ptr); uint128 initial_total_shares = node_total_shares; uint128 initial_total_value = node_total_value; node_total_shares += extra_total_shares; node_total_value += extra_total_value; saveSharesValueToArena(node_ptr, node_total_shares, node_total_value); extra_total_shares += initial_total_shares; extra_total_value += initial_total_value; if (node_id == stop_node_id) { break; } unchecked { // we are guaranteed to have a stop order higher mask = ~(map ^ (map - 1)); } } new_rightmost_map = uint64(map); } /// @dev Converts the regular right branch to rightmost branch starting from the given start_node_id. /// @param start_node_id The starting node_id for the conversion. Must me on the rightmost branch. /// @return xor_map The XOR diff with rightmost map generated during the conversion. function convertToRightmost(uint64 start_node_id) internal returns (uint64 xor_map) { xor_map = 0x1; // including leaf node uint64 node_id = start_node_id; Node memory node; while ((node_id & 0x1) != 0x1) { // untill we reach leaf node = nodes[node_id]; uint64 ptr = node.total_shares_value_ptr; uint64 child_node_id = node.right; (uint128 child_shares, uint128 child_value) = extractSharesValueForNode(child_node_id); removeFromArenaValues(ptr, child_shares, child_value); uint64 k; unchecked { k = ~(node_id - 1) & node_id; // Extracts the bit that determines the length. // Valid node always is greater than 0. } xor_map ^= k; node_id = child_node_id; } best_offer = node_id; } /// @dev Updates root node and adds a new order as a descendant of new root. /// @param order_id The ID of the new order. /// @param root_node_id The ID of the root node in the tree. /// @param shares The number of shares for the new order. /// @param value The value associated with the new order. function addOrderAndUpdateRoot(uint64 order_id, uint64 root_node_id, uint128 shares, uint128 value) internal { uint64 extra_node_id = calcCommonParent(order_id, root_node_id); Node memory extra_node; uint64 extra_node_k; unchecked { extra_node_k = ~(extra_node_id - 1) & extra_node_id; // Extracts the bit that determines the length. // Valid node always is greater than 0. } uint64 free_ptr = allocateMemory(); uint64 new_rightmost_map; if (root_node_id < order_id) { // inserting node to the right uint128 extra_total_shares; uint128 extra_total_value; (extra_total_shares, extra_total_value, new_rightmost_map) = normalizeBranch(root_node_id); saveSharesValueToArena(free_ptr, extra_total_shares, extra_total_value); extra_node = Node({ left: root_node_id, right: order_id, total_shares_value_ptr: free_ptr }); new_rightmost_map ^= extra_node_k ^ 0x1; // 0x1 for new leaf best_offer = order_id; } else { // inserting node to the left saveSharesValueToArena(free_ptr, shares, value); extra_node = Node({ left: order_id, right: root_node_id, total_shares_value_ptr: free_ptr }); new_rightmost_map = rightmost_map ^ extra_node_k; } rightmost_map = new_rightmost_map; nodes[extra_node_id] = extra_node; } /// @dev Adds an order to the nodes based on the provided order_id, node_id, shares, and value. /// @param order_id The ID of the order to add. /// @param node_id The ID of the node where the order should be added. Must be the lowest node in rightmost branch /// that matches order_id. /// @param shares The number of shares for the order. /// @param value The value associated with the order. function addOrderToNodes(uint64 order_id, uint64 node_id, uint128 shares, uint128 value) internal { // we now for sure that node_id is not a leaf Node memory node = nodes[node_id]; if (matchesNode(order_id, node.left)) { uint64 node_ptr = node.total_shares_value_ptr; addToArenaValues(node_ptr, shares, value); addOrderToNonRightmostNodeDescendants(node.left, order_id, shares, value); return; } // node.right can't match order_id // so no "else" logic needs here // so we are inserting new node here, in current node uint64 extra_node_id; Node memory extra_node; uint64 free_ptr = allocateMemory(); if (order_id < node_id) { uint64 node_ptr = node.total_shares_value_ptr; addToArenaValues(node_ptr, shares, value); extra_node_id = calcCommonParent(order_id, node.left); if (node.left > order_id) { extra_node = Node(order_id, node.left, free_ptr); } else { extra_node = Node(node.left, order_id, free_ptr); } (uint128 child_shares, uint128 child_value) = extractSharesValueForNode(node.left); saveSharesValueToArena(free_ptr, shares + child_shares, value + child_value); node.left = extra_node_id; } else { // here we must properly rebuild the rightmost branch extra_node_id = calcCommonParent(order_id, node.right); uint64 extra_node_k; unchecked { extra_node_k = ~(extra_node_id - 1) & extra_node_id; // Extracts the bit that determines the length. // Valid node always is greater than 0. } if (node.right > order_id) { saveSharesValueToArena(free_ptr, shares, value); extra_node = Node(order_id, node.right, free_ptr); rightmost_map ^= extra_node_k; } else { (uint128 extra_total_shares, uint128 extra_total_value, uint64 new_rightmost_map) = normalizeBranch(node.right); rightmost_map = new_rightmost_map ^ extra_node_k ^ 0x1; // 0x1 for new leaf; best_offer = order_id; saveSharesValueToArena(free_ptr, extra_total_shares, extra_total_value); extra_node = Node(node.right, order_id, free_ptr); } node.right = extra_node_id; } nodes[node_id] = node; nodes[extra_node_id] = extra_node; } /// @dev Adds an order to the descendants of a node that is not the rightmost node. /// @param node_id The ID of the node from which to start updating. It must match order_id. /// @param order_id The ID of the order to add. /// @param shares The total shares associated with the order. /// @param value The total value associated with the order. function addOrderToNonRightmostNodeDescendants( uint64 node_id, uint64 order_id, uint128 shares, uint128 value ) internal { uint64 next_node_id; Node memory node; // Descending the tree starting from node_id until neither child matches order_id, // indicating a new node insertion point. while (true) { node = nodes[node_id]; uint64 ptr = node.total_shares_value_ptr; addToArenaValues(ptr, shares, value); if (matchesNode(order_id, node.left)) { next_node_id = node.left; } else if (matchesNode(order_id, node.right)) { next_node_id = node.right; } else { break; } node_id = next_node_id; } uint64 extra_node_id; Node memory extra_node; uint64 free_ptr = allocateMemory(); uint64 child_node_id; if (order_id < node_id) { // Inserting new (extra) node to the left. extra_node_id = calcCommonParent(order_id, node.left); if (node.left > order_id) { extra_node = Node(order_id, node.left, free_ptr); } else { extra_node = Node(node.left, order_id, free_ptr); } child_node_id = node.left; node.left = extra_node_id; } else { // Inserting new (extra) node to the right. extra_node_id = calcCommonParent(order_id, node.right); if (node.right > order_id) { extra_node = Node(order_id, node.right, free_ptr); } else { extra_node = Node(node.right, order_id, free_ptr); } child_node_id = node.right; node.right = extra_node_id; } (uint128 child_shares, uint128 child_value) = extractSharesValueForNode(child_node_id); saveSharesValueToArena(free_ptr, shares + child_shares, value + child_value); nodes[node_id] = node; nodes[extra_node_id] = extra_node; } /// @dev Checks if a given order is present in the tree. /// @param node_id The ID of the starting node identifier from which the search begins. /// @param order_id The ID of the order to search for in the tree. /// @return isPresent A boolean value indicating whether the order is present in the tree. function isOrderInTree(uint64 node_id, uint64 order_id) internal view returns (bool isPresent) { if (node_id == order_id) { return true; } Node memory node; while (true) { node = nodes[node_id]; if (node.left == order_id || node.right == order_id) { return true; } if (matchesNode(order_id, node.left)) { node_id = node.left; } else if (matchesNode(order_id, node.right)) { node_id = node.right; } else { return false; } } } /// @dev Removes best offer order and rebuild rightmost branch if needed. function removeBestOffer() internal { uint64 local_best_offer = best_offer; uint64 map = rightmost_map; if (map == 0x1) { // This means there is only one order in the trie. // We remove it and leave the trie empty. rightmost_map = 0x0; best_offer = 0x0; return; } // We need to find the parent node and remove it as well. // It exists because there are more than one leaf in the tree. map ^= 0x1; uint64 mask; unchecked { // map > 0 since there are more than one leaf in the tree mask = ~(map ^ (map - 1)); } uint64 k = (((mask >> 1) | 0x8000000000000000) & ~mask); // Removing this node from rightmost map too. map ^= k; uint64 node_id = (local_best_offer & mask) ^ k; Node memory node = nodes[node_id]; eraseNode(node_id, node.total_shares_value_ptr); uint64 new_right_child = node.left; // and we have new rightmost branch uint64 new_rightmost_map = map ^ convertToRightmost(new_right_child); rightmost_map = new_rightmost_map; // This means that the node being removed has no "grandfather" node. if (new_rightmost_map == 0x1) { return; } // We also need to update the right child of the ancestor's ancestor of our order, as we have just removed it. unchecked { // map > 0 since there are at least one more ancestor mask = ~(map ^ (map - 1)); } k = (((mask >> 1) | 0x8000000000000000) & ~mask); node_id = (local_best_offer & mask) ^ k; nodes[node_id].right = new_right_child; } /// @dev Removes an order from the nodes based on the provided order_id, node_id, remaining shares, and remaining value. /// @param order_id The ID of the order to remove. /// @param node_id The ID of the node from which to remove the order. /// @param remain_shares The remaining shares of the order. /// @param remain_value The remaining value of the order. /// @return found Indicates if the order was found and removed. function removeOrderFromNodes( uint64 order_id, uint64 node_id, uint128 remain_shares, uint128 remain_value ) internal returns (bool found) { Node memory node = nodes[node_id]; if (node.left == order_id) { eraseNode(node_id, node.total_shares_value_ptr); uint64 k; unchecked { // valid node_id is always greater than 0 k = ~(node_id - 1) & node_id; } uint64 map = rightmost_map; rightmost_map = map ^ k; uint64 mask; unchecked { mask = ~(k ^ (k - 1)); // k > 0 } map = map & mask; if (map == 0x0) { // same case in removeBestOffer - no "grandfather" node. return true; } unchecked { k = ~(map - 1) & map; // we just checked that map > 0 mask = ~(k ^ (k - 1)); // k > 0 } uint64 new_right_child = node.right; node_id = (node_id & mask) ^ k; nodes[node_id].right = new_right_child; return true; } if (!matchesNode(order_id, node.left)) { return false; } uint64 returned_child; (found, returned_child) = removeOrderFromNonRightmostNodeDescendants( node.left, order_id, remain_shares, remain_value ); if (found) { removeFromArenaValues(node.total_shares_value_ptr, remain_shares, remain_value); if (node.left != returned_child){ node.left = returned_child; nodes[node_id] = node; } } } /// @dev Recursively removes an order from the descendants of a non-rightmost node. /// @param node_id The ID of the current node being processed. Must be not a leaf. /// @param order_id The ID of the order to be removed. /// @param shares The number of shares associated with the order. /// @param value The value associated with the order. /// @return found Indicates if the order was found and removed. /// @return returned_child The identifier of the child node returned after removal. function removeOrderFromNonRightmostNodeDescendants( uint64 node_id, uint64 order_id, uint128 shares, uint128 value ) internal returns (bool found, uint64 returned_child) { Node memory node = nodes[node_id]; if (node.left == order_id) { eraseNode(node_id, node.total_shares_value_ptr); return (true, node.right); } else if (node.right == order_id) { eraseNode(node_id, node.total_shares_value_ptr); return (true, node.left); } if (matchesNode(order_id, node.left)) { (found, returned_child) = removeOrderFromNonRightmostNodeDescendants(node.left, order_id, shares, value); if (!found) { return (false, 0x0); } removeFromArenaValues(node.total_shares_value_ptr, shares, value); if (node.left != returned_child){ node.left = returned_child; nodes[node_id] = node; } return (true, node_id); } else if (matchesNode(order_id, node.right)) { (found, returned_child) = removeOrderFromNonRightmostNodeDescendants(node.right, order_id, shares, value); if (!found) { return (false, 0x0); } removeFromArenaValues(node.total_shares_value_ptr, shares, value); if (node.left != returned_child){ node.right = returned_child; nodes[node_id] = node; } return (true, node_id); } else { return (false, 0x0); } } /// @dev Extracts key and mask from a given node_id. /// @param node_id The ID of the node from which to extract key and mask. /// @return key The extracted key. /// @return mask The extracted mask. function extractKeyAndMaskFrom(uint64 node_id) internal pure returns (uint64 key, uint64 mask) { unchecked { // valid node_id is always greater than 0 mask = ~((node_id - 1) ^ node_id); } key = node_id & mask; return (key, mask); } /// @dev Extracts key from a given node_id. /// @param node_id The ID of the node from which to extract key. /// @return key The extracted key. function extractKeyFrom(uint64 node_id) internal pure returns (uint64 key) { uint64 k; unchecked { // valid node_id is always greater than 0 k = ~(node_id - 1) & node_id; } return node_id ^ k; } /// @dev Calculates the common parent node for a given order_id and node_id. /// @param order_id The ID of the order to calculate the common parent for. /// @param node_id The ID of the node to calculate the common parent for. /// @return The calculated common parent node. function calcCommonParent(uint64 order_id, uint64 node_id) internal pure returns (uint64) { uint64 key = extractKeyFrom(node_id); uint256 diff = (order_id ^ 0x1) ^ key; uint256 mask = ~uint256(type(uint64).max); uint256 t; t = mask >> 32; if ((t & diff) == 0x0) { mask = t; } t = mask >> 16; if ((t & diff) == 0x0) { mask = t; } t = mask >> 8; if ((t & diff) == 0x0) { mask = t; } t = mask >> 4; if ((t & diff) == 0x0) { mask = t; } t = mask >> 2; if ((t & diff) == 0x0) { mask = t; } t = mask >> 1; if ((t & diff) == 0x0) { mask = t; } uint256 k = ((mask >> 1) & ~mask); return uint64((order_id & mask) ^ k); } /// @dev Checks if the order_id is a descendant of the node_id. /// @param order_id The ID of the order to compare with the node_id. /// @param node_id The ID of the node to compare with the order_id. /// @return True if the order_id matches the node_id, false otherwise. function matchesNode(uint64 order_id, uint64 node_id) internal pure returns (bool) { uint64 mask; unchecked { // valid node_id is always greater than 0 mask = ~((node_id - 1) ^ node_id); } return ((order_id ^ node_id) & mask) == 0x0; } /// @dev Erases the node with the specified node_id. /// @param node_id The ID of the node of the node to erase. function eraseNode(uint64 node_id, uint64 ptr) internal virtual { // Requires a more thoughtful strategy than just erasing everything. // if ((node_id & 0x1) != 0x1) { // delete nodes[node_id]; // } freeMemory(ptr); } /// @dev Recursively executes orders using a "right" approach within the order matching process. /// @param order_id The price identifier of the order, not necessarily associated with a real order ID. /// @param node_id The ID of the node within the order. /// @param rest_total_shares The remaining total shares to be executed. /// @return execution_result An ExecutionResult struct containing the result of the execution. function executeRightRec(uint64 order_id, uint64 node_id, uint128 rest_total_shares) internal returns (ExecutionResult memory execution_result) { if ((node_id & 0x1) == 0x1) { // leaf execution return executeLeaf(order_id, node_id, rest_total_shares); } // node execution Node memory node = nodes[node_id]; uint64 node_ptr = node.total_shares_value_ptr; (uint128 node_shares, uint128 node_value) = loadSharesValueFromArena(node_ptr); // node.left != 0x0 since it node, not a leaf uint64 price_key = extractKeyFrom(node.left); // the same condition in executeRight if (((order_id ^ 0x1) > price_key) || (rest_total_shares < node_shares)) { ExecutionResult memory right_execution_result = executeRightRec(order_id, node.right, rest_total_shares); rest_total_shares -= right_execution_result.executed_shares; if (right_execution_result.right_child != 0x0) { removeFromArenaValues( node_ptr, right_execution_result.total_shares + right_execution_result.executed_shares, right_execution_result.total_value + right_execution_result.executed_value ); if (node.right != right_execution_result.right_child) { node.right = right_execution_result.right_child; nodes[node_id] = node; } uint64 k; unchecked { k = ~(node_id - 1) & node_id; // Extracts the bit that determines the length. // Valid node always is greater than 0. } execution_result = ExecutionResult({ executed_shares: right_execution_result.executed_shares, executed_value: right_execution_result.executed_value, total_shares: node_shares - right_execution_result.executed_shares, total_value: node_value - right_execution_result.executed_value, right_child: node_id, xor_map: right_execution_result.xor_map ^ k }); } else { execution_result = executeRightRec(order_id, node.left, rest_total_shares); execution_result.executed_shares += right_execution_result.executed_shares; execution_result.executed_value += right_execution_result.executed_value; } } else { // full execution execution_result = ExecutionResult({ executed_shares: node_shares, executed_value: node_value, total_shares: 0, total_value: 0, right_child: 0x0, xor_map: 0x0 }); eraseNode(node_id, node_ptr); } } /// @dev Executes a leaf node within the order matching process. /// @param order_id The price identifier of the order, not associated with a real order ID. /// @param leaf_node_id The ID of the leaf node within the order. /// @param rest_total_shares The remaining total shares to be executed. /// @return execution_result An ExecutionResult struct containing the result of the execution. function executeLeaf(uint64 order_id, uint64 leaf_node_id, uint128 rest_total_shares) internal returns (ExecutionResult memory execution_result) { Leaf memory leaf = leaves[leaf_node_id]; (uint128 initial_shares, uint128 initial_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); uint128 remain_shares = leaf.remain_shares; uint128 remain_value = uint128(uint256(remain_shares) * uint256(initial_value) / uint256(initial_shares)); if (leaf_node_id >= order_id) { // both end on 0x1 bit, so we can compare them // we can execute current leaf if (remain_shares <= rest_total_shares) { // full execution execution_result = ExecutionResult({ executed_shares: remain_shares, executed_value: remain_value, total_shares: 0, total_value: 0, right_child: 0x0, xor_map: 0x0 }); } else { // partial execution uint128 executed_shares = rest_total_shares; // Since the operation node.total_value = node.total_shares * price is expected, // the quotient will always be an integer. uint128 executed_value = uint128(uint256(rest_total_shares) * uint256(remain_value) / uint256(remain_shares)); // remain_value / remain_shares = price unchecked { leaf.remain_shares -= executed_shares; } leaves[leaf_node_id] = leaf; execution_result = ExecutionResult({ executed_shares: executed_shares, executed_value: executed_value, total_shares: remain_shares - executed_shares, total_value: remain_value - executed_value, right_child: leaf_node_id, xor_map: 0x1 }); best_offer = leaf_node_id; } } else { // we can't execute current leaf at all execution_result = ExecutionResult({ executed_shares: 0, executed_value: 0, total_shares: remain_shares, total_value: remain_value, right_child: leaf_node_id, xor_map: 0x1 }); best_offer = leaf_node_id; } // A brief comment explaining why in some cases we equate the best offer to the current leaf. // When executing multiple orders, only one of them may be partially executed. This partially executed order // becomes the best offer. // An entirely unexecuted order becomes the best because our algorithm, encountering such behavior, // stops trying to execute other orders - they are guaranteed to be worse. On the other hand, // all better orders have already been fully executed. } /// @dev Preview the execution of a leaf node. /// @param order_id The ID of the order to be executed. /// @param leaf_node_id The ID of the leaf node to be executed. /// @param rest_shares The remaining shares of the order. /// @param rest_value The remaining value of shares of the order. /// @return executed_shares The number of shares executed. /// @return executed_value The value of the executed shares. /// @return full A boolean indicating whether the leaf was fully executed (true) or partially executed (false). function previewExecuteLeaf(uint64 order_id, uint64 leaf_node_id, uint128 rest_shares, uint128 rest_value) internal view returns (uint128 executed_shares, uint128 executed_value, bool full) { Leaf memory leaf = leaves[leaf_node_id]; (uint128 initial_shares, uint128 initial_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); uint128 leaf_shares = leaf.remain_shares; uint128 leaf_value = uint128(uint256(leaf_shares) * uint256(initial_value) / uint256(initial_shares)); if (leaf_node_id >= order_id) { // both end on 0x1 bit, so we can compare them // we can execute current leaf // The order of these checks is important and should not be changed. // We first check if we need to adjust shares based on value, // then we check if we need to adjust value based on shares. // This ensures correct handling of partial executions and rounding. if (rest_value < leaf_value) { uint128 shares = uint128(uint256(rest_value) * uint256(leaf_shares) / uint256(leaf_value)); // note: This division may not result in an integer, and rounding down is implied if (shares < rest_shares) { rest_shares = shares; } } if (rest_shares < leaf_shares) { uint128 value = uint128(uint256(rest_shares) * uint256(leaf_value) / uint256(leaf_shares)); if (value < rest_value) { rest_value = value; } } if (rest_shares < leaf_shares) { // partial execution return (rest_shares, rest_value, false); } else { // full execution return (leaf_shares, leaf_value, true); } } else { // we can't execute current leaf at all return (0, 0, false); } } /// @dev Calculates the next rightmost node ID and its associated k-bit based on the given node ID and map. /// @param node_id The node ID. /// @param map The map used for calculation. /// @return The next rightmost node ID and its associated k-bit. function calcNextRightMostNodeId(uint64 node_id, uint64 map) internal pure returns (uint64, uint64) { uint64 mask; unchecked { mask = ~(node_id ^ (node_id - 1)); // valid node_id is always greater than 0 map &= mask; } if (map == 0x0) { return (0, 0); } unchecked { mask = ~(map ^ (map - 1)); // we just checker map > 0 } uint64 k = (((mask >> 1) | 0x8000000000000000) & ~mask); return ((node_id & mask) ^ k, k); } /// @dev Packs two uint128 values into a single uint256. /// @param a The first uint128 value. /// @param b The second uint128 value. /// @return p The packed uint256 value. function packTwoUintsToWord(uint128 a, uint128 b) internal pure returns (uint256 p) { p = uint256(a) ^ (uint256(b) << 128); } /// @dev Unpacks a uint256 value into two uint128 values. /// @param p The packed uint256 value. /// @return a The first unpacked uint128 value. /// @return b The second unpacked uint128 value. function unPackTwoUintsFromWord(uint256 p) internal pure returns (uint128 a, uint128 b) { a = uint128(p & 0xffffffffffffffffffffffffffffffff); b = uint128(p >> 128); } /// @dev Allocates memory for storing data. /// @return ptr The pointer to the allocated memory. function allocateMemory() internal returns (uint64 ptr) { if (last_free_pointer == 0x0) { return (++highest_allocated_pointer); } ptr = last_free_pointer; last_free_pointer = uint64(arena[ptr]); } /// @dev Frees the memory at the given pointer. /// @param ptr The pointer to the memory to be freed. function freeMemory(uint64 ptr) internal { require(last_free_pointer != ptr, Errors.PointerAlreadyFreed()); arena[ptr] = packTwoUintsToWord(last_free_pointer, 0x1); last_free_pointer = ptr; } /// @dev Saves the shares and value to the memory at the given pointer. /// @param ptr The pointer to the memory where the data will be saved. /// @param shares The shares to be saved. /// @param value The value to be saved. function saveSharesValueToArena(uint64 ptr, uint128 shares, uint128 value) internal { arena[ptr] = packTwoUintsToWord(shares, value); } /// @dev Loads the shares and value from the memory at the given pointer. /// @param ptr The pointer to the memory from where the data will be loaded. /// @return The shares and value loaded from the memory. function loadSharesValueFromArena(uint64 ptr) internal view returns (uint128, uint128) { return unPackTwoUintsFromWord(arena[ptr]); } /// @dev Adds shares and value to the data at the given pointer in memory. /// @param ptr The pointer to the memory where the data will be added. /// @param adding_shares The shares to be added. /// @param adding_value The value to be added. function addToArenaValues(uint64 ptr, uint128 adding_shares, uint128 adding_value) internal { (uint128 shares, uint128 value) = unPackTwoUintsFromWord(arena[ptr]); shares += adding_shares; value += adding_value; arena[ptr] = packTwoUintsToWord(shares, value); } /// @dev Removes shares and value from the data at the given pointer in memory. /// @param ptr The pointer to the memory from where the data will be removed. /// @param removing_shares The shares to be removed. /// @param removing_value The value to be removed. function removeFromArenaValues(uint64 ptr, uint128 removing_shares, uint128 removing_value) internal { (uint128 shares, uint128 value) = unPackTwoUintsFromWord(arena[ptr]); shares -= removing_shares; value -= removing_value; arena[ptr] = packTwoUintsToWord(shares, value); } /// @dev Extracts the shares and value for a given node. /// @param node_id The ID of the node. /// @return The shares and value for the node. function extractSharesValueForNode(uint64 node_id) internal view returns (uint128, uint128) { if ((node_id & 0x1) == 0x1) { // leaf case Leaf memory leaf = leaves[node_id]; (uint128 initial_shares, uint128 initial_value) = loadSharesValueFromArena(leaf.initial_shares_value_ptr); uint128 remain_shares = leaf.remain_shares; uint128 remain_value = uint128(uint256(remain_shares) * uint256(initial_value) / uint256(initial_shares)); return (remain_shares, remain_value); } uint64 ptr = nodes[node_id].total_shares_value_ptr; return loadSharesValueFromArena(ptr); } }
// SPDX-License-Identifier: BUSL-1.1 // Central Limit Order Book (CLOB) exchange // (c) Long Gamma Labs, 2023. pragma solidity ^0.8.26; interface ITrieFactory { function createTrie(address lob) external returns (address); }
// SPDX-License-Identifier: BUSL-1.1 // Central Limit Order Book (CLOB) exchange // (c) Long Gamma Labs, 2023. pragma solidity ^0.8.26; contract Errors { error AddressIsZero(); error ArrayLengthMismatch(); error ChainIsUnstableForTrades(); error ClaimNotAllowed(); error CommissionParamTooHigh(); error Disabled(); error EmptyOrderError(); error ExcessiveSignificantFigures(); error Expired(); error Forbidden(); error FractionalNumbersNotAllowed(); error InsufficientTokenXBalance(); error InsufficientTokenYBalance(); error InvalidCommissionRate(); error InvalidFloatingPointRepresentation(); error InvalidMarketMaker(); error InvalidPriceRange(); error InvalidTransfer(); error MarketOnlyAndPostOnlyFlagsConflict(); error MaxCommissionFailure(); error NativeETHDisabled(); error NonceExhaustedFailure(); error NotImplementedYet(); error OnlyOwnerCanCancelOrders(); error PointerAlreadyFreed(); error PriceExceedsMaximumAllowedValue(); error TransferFailed(); error UnknownOrderId(); error UnknownTrader(); error WrongOwner(); error ZeroMaxDelayNotAllowed(); error ZeroRecoveryTimeNotAllowed(); error ZeroTokenTransferNotAllowed(); }
// SPDX-License-Identifier: BUSL-1.1 // Central Limit Order Book (CLOB) exchange // (c) Long Gamma Labs, 2023. pragma solidity ^0.8.26; interface ITrie { function best_offer() external view returns (uint64); function rightmost_map() external view returns (uint64); function addOrder(uint64 trader_id, uint64 order_id, uint128 shares, uint128 value) external; function removeOrder(uint64 order_id, uint64 order_trader_id) external returns (uint128 total_shares, uint128 remain_shares); function claimExecuted(uint64 order_id, uint64 order_trader_id) external returns (uint128 executed_shares, uint128 remain_shares); function getOrderInfo(uint64 order_id) external view returns (uint128 total_shares, uint128 remain_shares); function executeRight(uint64 order_id, uint128 order_total_shares) external returns (uint128 executed_shares, uint128 executed_value); function previewExecuteRight(uint64 order_id, uint128 max_shares, uint128 max_value) external view returns (uint128 executed_shares, uint128 executed_value); function assembleOrderbookFromOrders(uint24 max_price_levels) external view returns (uint24[] memory array_price_ids, uint128[] memory array_shares); }
{ "remappings": [ "forge-std/=lib/forge-std/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@solmate/src/=lib/solmate/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "cancun", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"lob","type":"address"}],"name":"createTrie","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60808060405234601557612e5d908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c6330b9c351146023575f80fd5b3460a857602036600319011260a8576004356001600160a01b0381169081900360a857612d7b80830183811067ffffffffffffffff82111760945760209284926100ad843981520301905ff080156089576040516001600160a01b039091168152602090f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60a03461008d57601f612d7b38819003918201601f19168301916001600160401b038311848410176100915780849260209460405283398101031261008d57516001600160a01b038116810361008d575f80546001600160801b0319169055608052604051612cd590816100a6823960805181818160a20152818161019a015281816101f601526102500152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c9081630d1cf14614610363575080632e2866831461033e5780635e9dd46a146102875780638f94fa521461022d578063a558898f146101d3578063b23264221461015b578063c4dca4221461013a578063e146af47146101085763fa1e9ec81461007f575f80fd5b3461010457604036600319011261010457610098610388565b6100a061039e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576100d9916111f8565b604080516001600160801b039384168152919092166020820152f35b631dd2188d60e31b5f5260045ffd5b5f80fd5b34610104576060366003190112610104576100d9610124610388565b61012c6103b4565b6101346103ca565b91610fd4565b34610104576020366003190112610104576100d9610156610388565b610f1e565b3461010457608036600319011261010457610174610388565b61017c61039e565b6101846103ca565b606435916001600160801b0383168303610104577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576101d193610d08565b005b34610104576040366003190112610104576101ec610388565b6101f46103b4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576100d991610985565b3461010457604036600319011261010457610246610388565b61024e61039e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576100d9916107af565b346101045760203660031901126101045760043562ffffff81168103610104576102b0906104f7565b90604051918291604083016040845281518091526020606085019201905f5b818110610320575050508281036020840152602080835192838152019201905f5b8181106102fe575050500390f35b82516001600160801b03168452859450602093840193909201916001016102f0565b825162ffffff168452869550602093840193909201916001016102cf565b34610104575f3660031901126101045760206001600160401b035f5416604051908152f35b34610104575f366003190112610104576020906001600160401b035f5460401c168152f35b600435906001600160401b038216820361010457565b602435906001600160401b038216820361010457565b602435906001600160801b038216820361010457565b604435906001600160801b038216820361010457565b60405190606082018281106001600160401b038211176103ff57604052565b634e487b7160e01b5f52604160045260245ffd5b6040519060c082018281106001600160401b038211176103ff57604052565b6040519190601f01601f191682016001600160401b038111838210176103ff57604052565b6001600160401b0381116103ff5760051b60200190565b9061048061047b83610457565b610432565b8281528092610491601f1991610457565b0190602036910137565b80518210156104af5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b906001600160801b03809116911601906001600160801b0382116104e357565b634e487b7160e01b5f52601160045260245ffd5b5f54916001600160401b0383169182158015610751575b6107245762ffffff16906105218261046e565b9061052b8361046e565b908251156104af576020839694960162ffffff8560281c16905260016001600160401b035f969560401c1618935b64ffffffffff8116156106ee5762ffffff8160281c1662ffffff871662ffffff610583828861049b565b511682036106a3575b50506105dc85826105a46001600160401b0394611323565b506001600160801b036105d36105cc62ffffff8d1693836105c5868d61049b565b51166104c3565b928961049b565b911690526113c7565b91169485156106045718935f5260026020526001600160401b0360405f205416955b95610559565b505092509262ffffff600191959295160162ffffff81116104e35762ffffff1690811061062e5750565b90916106398261046e565b6106428361046e565b945f5b62ffffff8116858110156106995762ffffff9181836106666001948761049b565b5116610672828861049b565b526106916001600160801b03610688838a61049b565b5116918b61049b565b520116610645565b5050509150509190565b90965062ffffff81146104e3576001019562ffffff87168881146106e1576001600160401b03926105dc926106d989938961049b565b52925061058c565b5050509350935090509190565b936001600160401b03908186815f198201161916161894165f5260026020526001600160401b0360405f205460401c16956105fe565b5091505060209061073482610432565b5f81525f36813761074483610432565b925f8452505f3681379190565b5062ffffff81161561050e565b906001600160801b03809116911603906001600160801b0382116104e357565b818102929181159184041417156104e357565b811561079b570490565b634e487b7160e01b5f52601260045260245ffd5b90916001600160401b038216805f52600160205260405f206107cf6103e0565b9054946001600160401b03861690818352604060208401936001600160801b0389831c168552019660c01c87528115610925576001600160401b031603610916576001600160801b039051169361082f6001600160401b03825116611428565b9590946001600160401b035f541684115f146108df5750505f935b610854858261075e565b956001600160801b038716156108d6578591906001600160801b038316908161089e57505050506001600160401b039061089c925f5260016020525f6040812055511661162b565b565b61089c95506001600160401b03926001600160801b036108c58194826108cd95169061077e565b911690610791565b169251166115fe565b505f9550505050565b6108eb8196929661144f565b1591908215610904575b50501561084a575f945061084a565b61090e92506114f2565b155f806108f5565b635d652eb160e01b5f5260045ffd5b505f9550859450505050565b906001600160401b036109426103e0565b92548181168452818160401c16602085015260801c166040830152565b90600160401b600160801b0382549160401b1690600160401b600160801b031916179055565b916001600160401b035f541691826001600160401b03851611610cff57825f52600160205260405f20936109b76103e0565b94546001600160401b038116865260208601906001600160801b038160401c16825260c01c956109ed6040820197808952611428565b9790916001600160801b03845116916001600160801b03871694838610610c5a575050506001600160801b0391826108c58382610a2c959c169061077e565b16610a456001600160401b035f5460401c1680976113c7565b906001915b6001600160401b038216928315610c4c5718915f52600260205260405f20986001600160401b03610a796103e0565b9a548181168c52818160401c1660208d015260801c16998a6040820152610a9f8b611428565b9290916001600160401b03610ac7818351166001600160401b0381815f198201161916161890565b166001600160401b0360018b1816111580610c30575b15610b125750610aff8b93610af9610b0a9694610b05946104c3565b976104c3565b9b61162b565b6113c7565b929892610a4a565b610b6991959c506001600160401b03935060809250610b4284610b54925116610b3b888d61075e565b908b611708565b956001600160801b03875116906104c3565b956001600160801b03602087015116906104c3565b9a8260a086015116189301511680610b86575087610b0a916113c7565b9450610ba09298979195506001600160401b0393506113c7565b50169081610c15575b50505b6001600160401b038116610bbd5750565b600160401b600160801b035f54916001600160401b038360401c161860401b16906001600160401b0382600160401b600160801b0319831617805f5560401c1615610c06575050565b6001600160801b031916175f55565b610c29915f52600260205260405f2061095f565b5f80610ba9565b506001600160801b03610c4383856104c3565b16881015610add565b965050505094505050610bac565b9250926001600160801b03919550610c8890826108c56001600160401b039782610cfb9b9c9d9e169061077e565b16976001600160801b038781845116031682525f526001602052828060405f2095511616831985541617845551600160401b600160c01b0384549160401b1690600160401b600160c01b03191617835551168154906001600160401b0360c01b9060c01b169060018060c01b0316179055565b9190565b505f9250829150565b92919092610d9f610d17611aec565b91610d238585856115fe565b6001600160401b03610d336103e0565b911681526001600160801b03841660208083019182526001600160401b0394851660408085019182528987165f8181526001909452928190209451935191516001600160c01b031960c09190911b1693909616600160401b600160c01b039190961b1694909417179055565b5f546001600160401b038160401c1615610f015750610dbd8461144f565b90919015610dcf575061089c93611b6d565b909291610ddf8461089c966127df565b92610de86114d8565b506001600160401b0384815f1982011619161694610e04611aec565b916001600160401b038216938585105f14610ec3575050946001600160401b0394939285600193610e37610e3f996128b1565b9a91836115fe565b610e476103e0565b9283528560208401521660408201529518189083195f5416175f555b600160401b600160801b035f549160401b1690600160401b600160801b031916175f55165f5260026020526001600160401b036040805f20928280825116168319855416178455610eba836020830151168561095f565b015116906116b1565b8392506001600160401b0396959491610edd9188956115fe565b610ee56103e0565b938452602084015216604082015292825f5460401c1618610e63565b6001600160801b0319161768010000000000000000175f55505050565b906040916001600160401b03811690815f526001602052835f20610f406103e0565b9054906001600160401b038216908181526001600160801b0383881c169283602083015260c01c968791015215610fc957610f7b9094611428565b5080926001600160401b035f541610610fc05750610f988161144f565b15610fb65790610fa7916114f2565b15610fb0579190565b91505f90565b5050909150905f90565b93505f92915050565b50505090505f905f90565b9291925f5f915f54956001600160401b038716806001600160401b0384161180156111e7575b80156111d6575b6111c857949660401c6001600160401b031660011893925b6001868116036110b2579461105a8661104b61103961105498998c61075e565b611043888761075e565b9085886120ca565b989190926104c3565b956104c3565b94156110a857611072866001600160401b03926113c7565b911695861561109d5718945f5260026020526001600160401b0360405f205416965b96949392611019565b505050509350919050565b5050509350919050565b92936001600160401b03861695865f5260026020526110e06001600160401b0360405f205460801c16611428565b90885f5260026020526001600160401b036111118160405f2054166001600160401b0381815f198201161916161890565b166001600160401b0360018818161180156111aa575b801561118c575b156111675750506001600160401b0390815f1982011619161618945f5260026020526001600160401b0360405f205460401c1696611094565b8398506001600160401b039391610af961118692611072959a996104c3565b966113c7565b50611197888661075e565b6001600160801b0380841691161061112e565b506111b5878c61075e565b6001600160801b03808316911610611127565b50505050505090505f905f90565b506001600160801b03821615611001565b506001600160801b03861615610ffa565b9190916001600160401b03811692835f52600160205260405f209161121b6103e0565b9254916001600160401b0383169384815260208101946001600160801b038560401c168652604082019460c01c855215611314576001600160401b0380915116911603610916576112756001600160401b03835116611428565b956112bd6001600160401b036001600160801b036112a661129d8280889b51169c168c61077e565b82871690610791565b1695835f5260016020525f6040812055511661162b565b6001600160401b035f5416918282116113085750146112fe576112df8161144f565b919091156112f357610fa7929186916123a7565b505050909150905f90565b5050610cfb6121fc565b96505f95945050505050565b63f37b890d60e01b5f5260045ffd5b60018082161461135a576001600160401b03165f5260026020526113566001600160401b0360405f205460801c16611428565b9091565b906001600160401b036001600160801b0392165f526001602052816113c360405f20826108c5816113b6604061138e6103e0565b9554956001600160401b038716815260208101968481841c16885260c01c9182910152611428565b989094511697168761077e565b1690565b91906001600160401b0390815f1985011684181916168015611420576001600160401b035f198201161819916001600160401b0380808516196001603f1b677fffffffffffffff8760011c161716169384921616189190565b505f91508190565b6001600160401b03165f52600360205260405f2054906001600160801b0382169160801c90565b905f546001600160401b038160401c166001600160401b038216600119915b6001600160401b03838784181616156114b957821691821561149857505f1982018218199161146e565b6001600160401b0394955084925080915060011c8119169216161816905f90565b50509081166001600160401b039081168219600193841c161816925090565b6114e06103e0565b905f82525f60208301525f6040830152565b90916001600160401b03831691826001600160401b038216146115f5576001600160401b03906115206114d8565b505b165f52600260205260405f206115366103e0565b9054906001600160401b038216908181526001600160401b03808460401c169384602084015260801c1660408201525083811480156115ec575b6115e25761159381866001600160401b039180835f198201161819911816161590565b156115a7576001600160401b039150611522565b506115c781856001600160401b039180835f198201161819911816161590565b156115da576001600160401b0390611522565b509150505f90565b5050915050600190565b50838214611570565b50915050600190565b91906001600160801b036001600160401b039281199060801b1691161891165f52600360205260405f2055565b5f5460c01c6001600160401b0382169081811461167657600160801b18905f52600360205260405f20555f54906001600160401b0360c01b9060c01b169060018060c01b0316175f55565b637fa3fb5160e01b5f5260045ffd5b61168d610413565b905f82525f60208301525f60408301525f60608301525f60808301525f60a0830152565b805467ffffffffffffffff60801b191660809290921b67ffffffffffffffff60801b16919091179055565b6001600160401b03604061089c938280825116168319855416178455610eba836020830151168561095f565b90929192611714611685565b50600180821614611ae15761174261173d826001600160401b03165f52600260205260405f2090565b610931565b9061175760408301516001600160401b031690565b61176081611428565b9490936001600160401b0361179561177f83516001600160401b031690565b6001600160401b0381815f198201161916161890565b166001600160401b036001841816118015611ac6575b15611a7257602081016117cf896117c983516001600160401b031690565b85611708565b6117eb90996117e58b516001600160801b031690565b9061075e565b60808a01936001600160401b0361180986516001600160401b031690565b16156119d75750506119869493611976938a936119669361188661185161183f6119969d9e9f604001516001600160801b031690565b88516001600160801b03165b906104c3565b9461186660608901516001600160801b031690565b9561188060208a019761184b89516001600160801b031690565b9161278c565b6118a861189a82516001600160401b031690565b93516001600160401b031690565b926001600160401b03808516911603611999575b5050506119566119036118d685516001600160801b031690565b9a6117e56118f58d6118ef87516001600160801b031690565b9c61075e565b94516001600160801b031690565b9361192a60a06001600160401b0389815f198201161916169201516001600160401b031690565b1897611946611937610413565b6001600160801b03909c168c52565b6001600160801b031660208b0152565b6001600160801b03166040890152565b6001600160801b03166060870152565b6001600160401b03166080850152565b6001600160401b031660a0830152565b90565b6119cf926119af91906001600160401b03169052565b6119ca876001600160401b03165f52600260205260405f2090565b6116dc565b5f80806118bc565b955095505090506119fb94506119f59150516001600160401b031690565b90611708565b611a4c602061089c9294611a3e611a31611a1c83516001600160801b031690565b88516001600160801b03166104c3565b6104c3565b6001600160801b03168752565b01516001600160801b031690565b611a656020850191611a2c83516001600160801b031690565b6001600160801b03169052565b5050945050611aa861089c92611a98611a89610413565b6001600160801b039094168452565b6001600160801b03166020830152565b5f60408201525f60608201525f60808201525f60a08201529261162b565b506001600160801b0385166001600160801b038916106117ab565b61199692939161258b565b5f548060c01c15611b2a57505f54908160c01c91825f5260036020526001600160401b0360c01b60405f205460c01b169060018060c01b0316175f55565b6001600160401b038160801c166001600160401b0381146104e357600101906001600160401b0360801b8260801b16906001600160401b0360801b1916175f5590565b906001600160401b031690815f52600260205260405f2092611b8d6103e0565b9354946001600160401b0386168086526001600160401b036020870197818160401c16895260801c16611bdc6040880192828452866001600160401b039180835f198201161819911816161590565b611e7e5750611be96114d8565b50611bf2611aec565b926001600160401b03851686811015611d5257908291611c1f85836001600160401b038c98975116612a04565b611c346001600160401b0386511680986127df565b9681811115611d06575084516001600160401b0316611c516103e0565b91825260208201526001600160401b03861660408201529761089c996001600160401b0398611ca3611cd2988b97611c9d611cca97611c97611c978c809a5b5116611323565b926104c3565b916115fe565b82891687525b5f526002602052818060405f2097511616821987541617865551168461095f565b5116906116b1565b165f5260026020526001600160401b036040805f20928280825116168319855416178455610eba836020830151168561095f565b90611d0f6103e0565b91825260208201526001600160401b03861660408201529761089c996001600160401b0398611ca3611cd2988b97611c9d611cca97611c97611c978c809a611c90565b9094959693889293611d726001600160401b0361089c9b511680996127df565b966001600160401b0388815f19820116191616988481115f14611e045750936001600160401b039899611cd29796948a85611db48299968397611cca996115fe565b81855116611dc06103e0565b93845260208401521660408201529a600160401b600160801b035f5491858360401c161860401b1690600160401b600160801b031916175f555b8289168252611ca9565b6001600160401b03809a8197935081945094611e5d611cca96600189600160401b600160801b03611e3a611cd29f9e9c986128b1565b92919490955f5493181860401b16906001600160801b03191617175f55836115fe565b8184511692611e6a6103e0565b938452602084015216604082015299611dfa565b8397506001600160401b039495508298969390611e9c939250612a04565b5116611ea66114d8565b505b6001600160401b0381165f52600260205260405f2090611ec66103e0565b9154916001600160401b03831681526001600160401b03806020830194818160401c16865260801c1691826040820152611f0289898395612a04565b511692611f2484866001600160401b039180835f198201161819911816161590565b15611f3157505050611ea8565b516001600160401b0390811693509091908484185f19850182168518191616611f5b575050611ea8565b61089c9593925095909395611f6e6114d8565b506001600160401b03611f7f611aec565b9116916001600160401b03841683811015612040578692611cd29492611c9d611ff593611fb96001600160401b03809b9c5116809a6127df565b9881811115612021575089875116611fcf6103e0565b91825260208201528984166040820152995b611c97611c978b8951168c8c168a52611323565b5f526002602052836040805f20928280825116168319855416178455610eba836020830151168561095f565b9061202a6103e0565b9182526020820152898416604082015299611fe1565b8692611cd29492611c9d6001600160401b0398611c97611c978b9a611ff59760206120739f019e8f9d8e511680936127df565b9c818311156120ab578e919250511661208a6103e0565b91825260208201528c871660408201529c5b8c815116908d8d169052611323565b506120b46103e0565b91825260208201528c871660408201529c61209c565b906001600160401b036001600160801b039195949516805f5260016020526001600160401b038261213860405f20826108c58161212b604061210a6103e0565b955495898716815260208101968481841c16885260c01c9182910152611428565b999094511698168861077e565b169316116121ef57806001600160801b0386168381106121b6575b6001600160801b038516848382109384612183575b505050505f1461217a57505091905f90565b93509160019150565b61219861219d926001600160801b039461077e565b610791565b169081106121ae575b808484612168565b95505f6121a6565b906121cd846121986001600160801b03938561077e565b166001600160801b03851681106121e7575b508190612153565b9350816121df565b50505090505f905f905f90565b5f546001600160401b038160401c1660018114612398576001600160401b036001815f19828518011683181819918180808516196001603f1b677fffffffffffffff8760011c161716168092189385161618165f5260026020526001600160401b0360405f2061228f8261226e6103e0565b92548181168452818160401c16602085015260801c1680604084015261162b565b51169081600161229d6114d8565b505b600180831603612335576001600160401b0390600192835f54928618189183600160401b600160801b038460401b169216906001600160801b03191617175f5516146123305761089c926001600160401b0360018193825f19838318011618181981808216196001603f1b677fffffffffffffff8460011c1617161692161618165f52600260205260405f2061095f565b505050565b6001600160401b0382165f5260026020526001600160401b0360405f209261238961235e6103e0565b945494838616815283808760401c169687602084015260801c16908160408201525061188086611323565b5f19810182161916161861229f565b506001600160801b0319165f55565b9392936001600160401b03821691825f52600260205260405f20926123ca6103e0565b9354926001600160401b038416928386526001600160401b036020870195818160401c16875260801c1660408701948186526001600160401b038416146124c757505084516001600160401b03908116908282185f198301821683181916166124bb5786612439928a92612a3e565b9590978861244b575b50505050505050565b61245f916001600160401b0385511661278c565b6001600160401b0380855116951680950361247c575b8080612442565b6001600160401b038093611cca926124b19787525f526002602052818060405f2097511616821987541617865551168461095f565b5f80808080612475565b505f9750505050505050565b9497985095505050506124da915061162b565b6001600160401b0381815f198201161916165f54908160401c91600160401b600160801b03826001600160401b0385161860401b1690600160401b600160801b031916175f556001600160401b035f19820116181916916001600160401b038316908115612582576001600160401b0380808061257d978180975f1901161916161692511693815f19840116831819161618165f52600260205260405f2061095f565b600190565b50505050600190565b90916001600160401b03612613936125a1611685565b501690815f52600160205260405f20906125b96103e0565b9154906001600160401b038216835260208301916001600160801b038160401c16835260c01c926001600160401b036001600160801b036126006040840196808852611428565b9990826108c581808a51169d168d61077e565b169616851061274a576001600160801b0382169384881161265f5750505050505061263c610413565b91825260208201525f60408201525f60608201525f60808201525f60a082015290565b9461270887938961270361270e956001600160401b036001600160801b039b879f9e6126958d9f918f9d9e8e936121989161077e565b169b8c988c8881845116031682525f526001602052828060405f2095511616831985541617845551600160401b600160c01b0384549160401b1690600160401b600160c01b03191617835551168154906001600160401b0360c01b9060c01b169060018060c01b0316179055565b61075e565b9261075e565b92612717610413565b9586526020860152166040840152166060820152826080820152600160a0820152916001600160401b03195f5416175f55565b50505050929190612759610413565b915f83525f602084015260408301526060820152826080820152600160a0820152916001600160401b03195f5416175f55565b6127c66001600160401b036001600160801b03921693845f5260036020526127c060405f2054948486169560801c9561075e565b9361075e565b81199060801b16911618905f52600360205260405f2055565b6001600160401b0391829067ffffffffffffffff19905f1981018316198116831618831860011867ffffffff0000000081161561289e575b8160101c838282161615612896575b508160081c83828216161561288e575b508160041c838282161615612886575b508160021c83828216161561287e575b50828260011c9182161615612876575b5080198160011c16921616181690565b90505f612866565b91505f612856565b91505f612846565b91505f612836565b91505f612826565b640100000000600160e01b039150612817565b6001600160801b035f54926001600160401b038460401c169283906001600160401b03861691825f5260016020526001600160401b0360018661293160405f20826108c58161292460406129036103e0565b9554958a8716815260208101968481841c16885260c01c9182910152611428565b9d909451169c168c61077e565b16971892169283146129f1576129b2611a2c6129ac6001600160401b03600187955f9b9a9b505f198801181819955b818760011c881916809218978d1616181698895f5260026020526001600160401b0360405f205460801c169061299582611428565b9490926129a283856104c3565b611c9d8d886104c3565b966104c3565b95146129df579293925f1981018118199082906129b290611a2c906129ac906001600160401b0390612960565b90506001600160401b03919294501690565b50929450506001600160401b0390911690565b6127c66001600160401b036001600160801b03921693845f526003602052612a3860405f2054948486169560801c956104c3565b936104c3565b9190612a5e61173d846001600160401b03165f52600260205260405f2090565b936001600160401b03612a7886516001600160401b031690565b83821691168103612abd575050505050602081612aaa612aa56040612ab89501516001600160401b031690565b61162b565b01516001600160401b031690565b600191565b612ae4612ad86020889697999801516001600160401b031690565b6001600160401b031690565b14612c715783612b19612afe85516001600160401b031690565b846001600160401b039180835f198201161819911816161590565b15612bc15790612b3b918193612b3686516001600160401b031690565b612a3e565b939015612bb55790612b6091612b5b60408501516001600160401b031690565b61278c565b80516001600160401b03838116911603612b7d575b505060019190565b612b93612bae9282906001600160401b03169052565b6119ca846001600160401b03165f52600260205260405f2090565b5f80612b75565b5050505090505f905f90565b506020830191612bd883516001600160401b031690565b612bf781836001600160401b039180835f198201161819911816161590565b156111c85785612c08928492612a3e565b949015612c645790612c2891612b5b60408601516001600160401b031690565b81516001600160401b03848116911603612c46575b50505060019190565b612c5c92612b9391906001600160401b03169052565b5f8080612c3d565b505050505090505f905f90565b50509050612ab8919250612c92612aa560408301516001600160401b031690565b516001600160401b03169056fea26469706673582212208cfcacc5e1a529ebf0fcdf9254e84504f7511e5db7c567bdad1b03249b26671764736f6c634300081c0033a2646970667358221220d6de167b9393a116407f94a7de5fd4b2dfbc1c6eeb2d7df3f323e0e632566f5b64736f6c634300081c0033
Deployed Bytecode
0x60808060405260043610156011575f80fd5b5f3560e01c6330b9c351146023575f80fd5b3460a857602036600319011260a8576004356001600160a01b0381169081900360a857612d7b80830183811067ffffffffffffffff82111760945760209284926100ad843981520301905ff080156089576040516001600160a01b039091168152602090f35b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b5f80fdfe60a03461008d57601f612d7b38819003918201601f19168301916001600160401b038311848410176100915780849260209460405283398101031261008d57516001600160a01b038116810361008d575f80546001600160801b0319169055608052604051612cd590816100a6823960805181818160a20152818161019a015281816101f601526102500152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c9081630d1cf14614610363575080632e2866831461033e5780635e9dd46a146102875780638f94fa521461022d578063a558898f146101d3578063b23264221461015b578063c4dca4221461013a578063e146af47146101085763fa1e9ec81461007f575f80fd5b3461010457604036600319011261010457610098610388565b6100a061039e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576100d9916111f8565b604080516001600160801b039384168152919092166020820152f35b631dd2188d60e31b5f5260045ffd5b5f80fd5b34610104576060366003190112610104576100d9610124610388565b61012c6103b4565b6101346103ca565b91610fd4565b34610104576020366003190112610104576100d9610156610388565b610f1e565b3461010457608036600319011261010457610174610388565b61017c61039e565b6101846103ca565b606435916001600160801b0383168303610104577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576101d193610d08565b005b34610104576040366003190112610104576101ec610388565b6101f46103b4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576100d991610985565b3461010457604036600319011261010457610246610388565b61024e61039e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036100f5576100d9916107af565b346101045760203660031901126101045760043562ffffff81168103610104576102b0906104f7565b90604051918291604083016040845281518091526020606085019201905f5b818110610320575050508281036020840152602080835192838152019201905f5b8181106102fe575050500390f35b82516001600160801b03168452859450602093840193909201916001016102f0565b825162ffffff168452869550602093840193909201916001016102cf565b34610104575f3660031901126101045760206001600160401b035f5416604051908152f35b34610104575f366003190112610104576020906001600160401b035f5460401c168152f35b600435906001600160401b038216820361010457565b602435906001600160401b038216820361010457565b602435906001600160801b038216820361010457565b604435906001600160801b038216820361010457565b60405190606082018281106001600160401b038211176103ff57604052565b634e487b7160e01b5f52604160045260245ffd5b6040519060c082018281106001600160401b038211176103ff57604052565b6040519190601f01601f191682016001600160401b038111838210176103ff57604052565b6001600160401b0381116103ff5760051b60200190565b9061048061047b83610457565b610432565b8281528092610491601f1991610457565b0190602036910137565b80518210156104af5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b906001600160801b03809116911601906001600160801b0382116104e357565b634e487b7160e01b5f52601160045260245ffd5b5f54916001600160401b0383169182158015610751575b6107245762ffffff16906105218261046e565b9061052b8361046e565b908251156104af576020839694960162ffffff8560281c16905260016001600160401b035f969560401c1618935b64ffffffffff8116156106ee5762ffffff8160281c1662ffffff871662ffffff610583828861049b565b511682036106a3575b50506105dc85826105a46001600160401b0394611323565b506001600160801b036105d36105cc62ffffff8d1693836105c5868d61049b565b51166104c3565b928961049b565b911690526113c7565b91169485156106045718935f5260026020526001600160401b0360405f205416955b95610559565b505092509262ffffff600191959295160162ffffff81116104e35762ffffff1690811061062e5750565b90916106398261046e565b6106428361046e565b945f5b62ffffff8116858110156106995762ffffff9181836106666001948761049b565b5116610672828861049b565b526106916001600160801b03610688838a61049b565b5116918b61049b565b520116610645565b5050509150509190565b90965062ffffff81146104e3576001019562ffffff87168881146106e1576001600160401b03926105dc926106d989938961049b565b52925061058c565b5050509350935090509190565b936001600160401b03908186815f198201161916161894165f5260026020526001600160401b0360405f205460401c16956105fe565b5091505060209061073482610432565b5f81525f36813761074483610432565b925f8452505f3681379190565b5062ffffff81161561050e565b906001600160801b03809116911603906001600160801b0382116104e357565b818102929181159184041417156104e357565b811561079b570490565b634e487b7160e01b5f52601260045260245ffd5b90916001600160401b038216805f52600160205260405f206107cf6103e0565b9054946001600160401b03861690818352604060208401936001600160801b0389831c168552019660c01c87528115610925576001600160401b031603610916576001600160801b039051169361082f6001600160401b03825116611428565b9590946001600160401b035f541684115f146108df5750505f935b610854858261075e565b956001600160801b038716156108d6578591906001600160801b038316908161089e57505050506001600160401b039061089c925f5260016020525f6040812055511661162b565b565b61089c95506001600160401b03926001600160801b036108c58194826108cd95169061077e565b911690610791565b169251166115fe565b505f9550505050565b6108eb8196929661144f565b1591908215610904575b50501561084a575f945061084a565b61090e92506114f2565b155f806108f5565b635d652eb160e01b5f5260045ffd5b505f9550859450505050565b906001600160401b036109426103e0565b92548181168452818160401c16602085015260801c166040830152565b90600160401b600160801b0382549160401b1690600160401b600160801b031916179055565b916001600160401b035f541691826001600160401b03851611610cff57825f52600160205260405f20936109b76103e0565b94546001600160401b038116865260208601906001600160801b038160401c16825260c01c956109ed6040820197808952611428565b9790916001600160801b03845116916001600160801b03871694838610610c5a575050506001600160801b0391826108c58382610a2c959c169061077e565b16610a456001600160401b035f5460401c1680976113c7565b906001915b6001600160401b038216928315610c4c5718915f52600260205260405f20986001600160401b03610a796103e0565b9a548181168c52818160401c1660208d015260801c16998a6040820152610a9f8b611428565b9290916001600160401b03610ac7818351166001600160401b0381815f198201161916161890565b166001600160401b0360018b1816111580610c30575b15610b125750610aff8b93610af9610b0a9694610b05946104c3565b976104c3565b9b61162b565b6113c7565b929892610a4a565b610b6991959c506001600160401b03935060809250610b4284610b54925116610b3b888d61075e565b908b611708565b956001600160801b03875116906104c3565b956001600160801b03602087015116906104c3565b9a8260a086015116189301511680610b86575087610b0a916113c7565b9450610ba09298979195506001600160401b0393506113c7565b50169081610c15575b50505b6001600160401b038116610bbd5750565b600160401b600160801b035f54916001600160401b038360401c161860401b16906001600160401b0382600160401b600160801b0319831617805f5560401c1615610c06575050565b6001600160801b031916175f55565b610c29915f52600260205260405f2061095f565b5f80610ba9565b506001600160801b03610c4383856104c3565b16881015610add565b965050505094505050610bac565b9250926001600160801b03919550610c8890826108c56001600160401b039782610cfb9b9c9d9e169061077e565b16976001600160801b038781845116031682525f526001602052828060405f2095511616831985541617845551600160401b600160c01b0384549160401b1690600160401b600160c01b03191617835551168154906001600160401b0360c01b9060c01b169060018060c01b0316179055565b9190565b505f9250829150565b92919092610d9f610d17611aec565b91610d238585856115fe565b6001600160401b03610d336103e0565b911681526001600160801b03841660208083019182526001600160401b0394851660408085019182528987165f8181526001909452928190209451935191516001600160c01b031960c09190911b1693909616600160401b600160c01b039190961b1694909417179055565b5f546001600160401b038160401c1615610f015750610dbd8461144f565b90919015610dcf575061089c93611b6d565b909291610ddf8461089c966127df565b92610de86114d8565b506001600160401b0384815f1982011619161694610e04611aec565b916001600160401b038216938585105f14610ec3575050946001600160401b0394939285600193610e37610e3f996128b1565b9a91836115fe565b610e476103e0565b9283528560208401521660408201529518189083195f5416175f555b600160401b600160801b035f549160401b1690600160401b600160801b031916175f55165f5260026020526001600160401b036040805f20928280825116168319855416178455610eba836020830151168561095f565b015116906116b1565b8392506001600160401b0396959491610edd9188956115fe565b610ee56103e0565b938452602084015216604082015292825f5460401c1618610e63565b6001600160801b0319161768010000000000000000175f55505050565b906040916001600160401b03811690815f526001602052835f20610f406103e0565b9054906001600160401b038216908181526001600160801b0383881c169283602083015260c01c968791015215610fc957610f7b9094611428565b5080926001600160401b035f541610610fc05750610f988161144f565b15610fb65790610fa7916114f2565b15610fb0579190565b91505f90565b5050909150905f90565b93505f92915050565b50505090505f905f90565b9291925f5f915f54956001600160401b038716806001600160401b0384161180156111e7575b80156111d6575b6111c857949660401c6001600160401b031660011893925b6001868116036110b2579461105a8661104b61103961105498998c61075e565b611043888761075e565b9085886120ca565b989190926104c3565b956104c3565b94156110a857611072866001600160401b03926113c7565b911695861561109d5718945f5260026020526001600160401b0360405f205416965b96949392611019565b505050509350919050565b5050509350919050565b92936001600160401b03861695865f5260026020526110e06001600160401b0360405f205460801c16611428565b90885f5260026020526001600160401b036111118160405f2054166001600160401b0381815f198201161916161890565b166001600160401b0360018818161180156111aa575b801561118c575b156111675750506001600160401b0390815f1982011619161618945f5260026020526001600160401b0360405f205460401c1696611094565b8398506001600160401b039391610af961118692611072959a996104c3565b966113c7565b50611197888661075e565b6001600160801b0380841691161061112e565b506111b5878c61075e565b6001600160801b03808316911610611127565b50505050505090505f905f90565b506001600160801b03821615611001565b506001600160801b03861615610ffa565b9190916001600160401b03811692835f52600160205260405f209161121b6103e0565b9254916001600160401b0383169384815260208101946001600160801b038560401c168652604082019460c01c855215611314576001600160401b0380915116911603610916576112756001600160401b03835116611428565b956112bd6001600160401b036001600160801b036112a661129d8280889b51169c168c61077e565b82871690610791565b1695835f5260016020525f6040812055511661162b565b6001600160401b035f5416918282116113085750146112fe576112df8161144f565b919091156112f357610fa7929186916123a7565b505050909150905f90565b5050610cfb6121fc565b96505f95945050505050565b63f37b890d60e01b5f5260045ffd5b60018082161461135a576001600160401b03165f5260026020526113566001600160401b0360405f205460801c16611428565b9091565b906001600160401b036001600160801b0392165f526001602052816113c360405f20826108c5816113b6604061138e6103e0565b9554956001600160401b038716815260208101968481841c16885260c01c9182910152611428565b989094511697168761077e565b1690565b91906001600160401b0390815f1985011684181916168015611420576001600160401b035f198201161819916001600160401b0380808516196001603f1b677fffffffffffffff8760011c161716169384921616189190565b505f91508190565b6001600160401b03165f52600360205260405f2054906001600160801b0382169160801c90565b905f546001600160401b038160401c166001600160401b038216600119915b6001600160401b03838784181616156114b957821691821561149857505f1982018218199161146e565b6001600160401b0394955084925080915060011c8119169216161816905f90565b50509081166001600160401b039081168219600193841c161816925090565b6114e06103e0565b905f82525f60208301525f6040830152565b90916001600160401b03831691826001600160401b038216146115f5576001600160401b03906115206114d8565b505b165f52600260205260405f206115366103e0565b9054906001600160401b038216908181526001600160401b03808460401c169384602084015260801c1660408201525083811480156115ec575b6115e25761159381866001600160401b039180835f198201161819911816161590565b156115a7576001600160401b039150611522565b506115c781856001600160401b039180835f198201161819911816161590565b156115da576001600160401b0390611522565b509150505f90565b5050915050600190565b50838214611570565b50915050600190565b91906001600160801b036001600160401b039281199060801b1691161891165f52600360205260405f2055565b5f5460c01c6001600160401b0382169081811461167657600160801b18905f52600360205260405f20555f54906001600160401b0360c01b9060c01b169060018060c01b0316175f55565b637fa3fb5160e01b5f5260045ffd5b61168d610413565b905f82525f60208301525f60408301525f60608301525f60808301525f60a0830152565b805467ffffffffffffffff60801b191660809290921b67ffffffffffffffff60801b16919091179055565b6001600160401b03604061089c938280825116168319855416178455610eba836020830151168561095f565b90929192611714611685565b50600180821614611ae15761174261173d826001600160401b03165f52600260205260405f2090565b610931565b9061175760408301516001600160401b031690565b61176081611428565b9490936001600160401b0361179561177f83516001600160401b031690565b6001600160401b0381815f198201161916161890565b166001600160401b036001841816118015611ac6575b15611a7257602081016117cf896117c983516001600160401b031690565b85611708565b6117eb90996117e58b516001600160801b031690565b9061075e565b60808a01936001600160401b0361180986516001600160401b031690565b16156119d75750506119869493611976938a936119669361188661185161183f6119969d9e9f604001516001600160801b031690565b88516001600160801b03165b906104c3565b9461186660608901516001600160801b031690565b9561188060208a019761184b89516001600160801b031690565b9161278c565b6118a861189a82516001600160401b031690565b93516001600160401b031690565b926001600160401b03808516911603611999575b5050506119566119036118d685516001600160801b031690565b9a6117e56118f58d6118ef87516001600160801b031690565b9c61075e565b94516001600160801b031690565b9361192a60a06001600160401b0389815f198201161916169201516001600160401b031690565b1897611946611937610413565b6001600160801b03909c168c52565b6001600160801b031660208b0152565b6001600160801b03166040890152565b6001600160801b03166060870152565b6001600160401b03166080850152565b6001600160401b031660a0830152565b90565b6119cf926119af91906001600160401b03169052565b6119ca876001600160401b03165f52600260205260405f2090565b6116dc565b5f80806118bc565b955095505090506119fb94506119f59150516001600160401b031690565b90611708565b611a4c602061089c9294611a3e611a31611a1c83516001600160801b031690565b88516001600160801b03166104c3565b6104c3565b6001600160801b03168752565b01516001600160801b031690565b611a656020850191611a2c83516001600160801b031690565b6001600160801b03169052565b5050945050611aa861089c92611a98611a89610413565b6001600160801b039094168452565b6001600160801b03166020830152565b5f60408201525f60608201525f60808201525f60a08201529261162b565b506001600160801b0385166001600160801b038916106117ab565b61199692939161258b565b5f548060c01c15611b2a57505f54908160c01c91825f5260036020526001600160401b0360c01b60405f205460c01b169060018060c01b0316175f55565b6001600160401b038160801c166001600160401b0381146104e357600101906001600160401b0360801b8260801b16906001600160401b0360801b1916175f5590565b906001600160401b031690815f52600260205260405f2092611b8d6103e0565b9354946001600160401b0386168086526001600160401b036020870197818160401c16895260801c16611bdc6040880192828452866001600160401b039180835f198201161819911816161590565b611e7e5750611be96114d8565b50611bf2611aec565b926001600160401b03851686811015611d5257908291611c1f85836001600160401b038c98975116612a04565b611c346001600160401b0386511680986127df565b9681811115611d06575084516001600160401b0316611c516103e0565b91825260208201526001600160401b03861660408201529761089c996001600160401b0398611ca3611cd2988b97611c9d611cca97611c97611c978c809a5b5116611323565b926104c3565b916115fe565b82891687525b5f526002602052818060405f2097511616821987541617865551168461095f565b5116906116b1565b165f5260026020526001600160401b036040805f20928280825116168319855416178455610eba836020830151168561095f565b90611d0f6103e0565b91825260208201526001600160401b03861660408201529761089c996001600160401b0398611ca3611cd2988b97611c9d611cca97611c97611c978c809a611c90565b9094959693889293611d726001600160401b0361089c9b511680996127df565b966001600160401b0388815f19820116191616988481115f14611e045750936001600160401b039899611cd29796948a85611db48299968397611cca996115fe565b81855116611dc06103e0565b93845260208401521660408201529a600160401b600160801b035f5491858360401c161860401b1690600160401b600160801b031916175f555b8289168252611ca9565b6001600160401b03809a8197935081945094611e5d611cca96600189600160401b600160801b03611e3a611cd29f9e9c986128b1565b92919490955f5493181860401b16906001600160801b03191617175f55836115fe565b8184511692611e6a6103e0565b938452602084015216604082015299611dfa565b8397506001600160401b039495508298969390611e9c939250612a04565b5116611ea66114d8565b505b6001600160401b0381165f52600260205260405f2090611ec66103e0565b9154916001600160401b03831681526001600160401b03806020830194818160401c16865260801c1691826040820152611f0289898395612a04565b511692611f2484866001600160401b039180835f198201161819911816161590565b15611f3157505050611ea8565b516001600160401b0390811693509091908484185f19850182168518191616611f5b575050611ea8565b61089c9593925095909395611f6e6114d8565b506001600160401b03611f7f611aec565b9116916001600160401b03841683811015612040578692611cd29492611c9d611ff593611fb96001600160401b03809b9c5116809a6127df565b9881811115612021575089875116611fcf6103e0565b91825260208201528984166040820152995b611c97611c978b8951168c8c168a52611323565b5f526002602052836040805f20928280825116168319855416178455610eba836020830151168561095f565b9061202a6103e0565b9182526020820152898416604082015299611fe1565b8692611cd29492611c9d6001600160401b0398611c97611c978b9a611ff59760206120739f019e8f9d8e511680936127df565b9c818311156120ab578e919250511661208a6103e0565b91825260208201528c871660408201529c5b8c815116908d8d169052611323565b506120b46103e0565b91825260208201528c871660408201529c61209c565b906001600160401b036001600160801b039195949516805f5260016020526001600160401b038261213860405f20826108c58161212b604061210a6103e0565b955495898716815260208101968481841c16885260c01c9182910152611428565b999094511698168861077e565b169316116121ef57806001600160801b0386168381106121b6575b6001600160801b038516848382109384612183575b505050505f1461217a57505091905f90565b93509160019150565b61219861219d926001600160801b039461077e565b610791565b169081106121ae575b808484612168565b95505f6121a6565b906121cd846121986001600160801b03938561077e565b166001600160801b03851681106121e7575b508190612153565b9350816121df565b50505090505f905f905f90565b5f546001600160401b038160401c1660018114612398576001600160401b036001815f19828518011683181819918180808516196001603f1b677fffffffffffffff8760011c161716168092189385161618165f5260026020526001600160401b0360405f2061228f8261226e6103e0565b92548181168452818160401c16602085015260801c1680604084015261162b565b51169081600161229d6114d8565b505b600180831603612335576001600160401b0390600192835f54928618189183600160401b600160801b038460401b169216906001600160801b03191617175f5516146123305761089c926001600160401b0360018193825f19838318011618181981808216196001603f1b677fffffffffffffff8460011c1617161692161618165f52600260205260405f2061095f565b505050565b6001600160401b0382165f5260026020526001600160401b0360405f209261238961235e6103e0565b945494838616815283808760401c169687602084015260801c16908160408201525061188086611323565b5f19810182161916161861229f565b506001600160801b0319165f55565b9392936001600160401b03821691825f52600260205260405f20926123ca6103e0565b9354926001600160401b038416928386526001600160401b036020870195818160401c16875260801c1660408701948186526001600160401b038416146124c757505084516001600160401b03908116908282185f198301821683181916166124bb5786612439928a92612a3e565b9590978861244b575b50505050505050565b61245f916001600160401b0385511661278c565b6001600160401b0380855116951680950361247c575b8080612442565b6001600160401b038093611cca926124b19787525f526002602052818060405f2097511616821987541617865551168461095f565b5f80808080612475565b505f9750505050505050565b9497985095505050506124da915061162b565b6001600160401b0381815f198201161916165f54908160401c91600160401b600160801b03826001600160401b0385161860401b1690600160401b600160801b031916175f556001600160401b035f19820116181916916001600160401b038316908115612582576001600160401b0380808061257d978180975f1901161916161692511693815f19840116831819161618165f52600260205260405f2061095f565b600190565b50505050600190565b90916001600160401b03612613936125a1611685565b501690815f52600160205260405f20906125b96103e0565b9154906001600160401b038216835260208301916001600160801b038160401c16835260c01c926001600160401b036001600160801b036126006040840196808852611428565b9990826108c581808a51169d168d61077e565b169616851061274a576001600160801b0382169384881161265f5750505050505061263c610413565b91825260208201525f60408201525f60608201525f60808201525f60a082015290565b9461270887938961270361270e956001600160401b036001600160801b039b879f9e6126958d9f918f9d9e8e936121989161077e565b169b8c988c8881845116031682525f526001602052828060405f2095511616831985541617845551600160401b600160c01b0384549160401b1690600160401b600160c01b03191617835551168154906001600160401b0360c01b9060c01b169060018060c01b0316179055565b61075e565b9261075e565b92612717610413565b9586526020860152166040840152166060820152826080820152600160a0820152916001600160401b03195f5416175f55565b50505050929190612759610413565b915f83525f602084015260408301526060820152826080820152600160a0820152916001600160401b03195f5416175f55565b6127c66001600160401b036001600160801b03921693845f5260036020526127c060405f2054948486169560801c9561075e565b9361075e565b81199060801b16911618905f52600360205260405f2055565b6001600160401b0391829067ffffffffffffffff19905f1981018316198116831618831860011867ffffffff0000000081161561289e575b8160101c838282161615612896575b508160081c83828216161561288e575b508160041c838282161615612886575b508160021c83828216161561287e575b50828260011c9182161615612876575b5080198160011c16921616181690565b90505f612866565b91505f612856565b91505f612846565b91505f612836565b91505f612826565b640100000000600160e01b039150612817565b6001600160801b035f54926001600160401b038460401c169283906001600160401b03861691825f5260016020526001600160401b0360018661293160405f20826108c58161292460406129036103e0565b9554958a8716815260208101968481841c16885260c01c9182910152611428565b9d909451169c168c61077e565b16971892169283146129f1576129b2611a2c6129ac6001600160401b03600187955f9b9a9b505f198801181819955b818760011c881916809218978d1616181698895f5260026020526001600160401b0360405f205460801c169061299582611428565b9490926129a283856104c3565b611c9d8d886104c3565b966104c3565b95146129df579293925f1981018118199082906129b290611a2c906129ac906001600160401b0390612960565b90506001600160401b03919294501690565b50929450506001600160401b0390911690565b6127c66001600160401b036001600160801b03921693845f526003602052612a3860405f2054948486169560801c956104c3565b936104c3565b9190612a5e61173d846001600160401b03165f52600260205260405f2090565b936001600160401b03612a7886516001600160401b031690565b83821691168103612abd575050505050602081612aaa612aa56040612ab89501516001600160401b031690565b61162b565b01516001600160401b031690565b600191565b612ae4612ad86020889697999801516001600160401b031690565b6001600160401b031690565b14612c715783612b19612afe85516001600160401b031690565b846001600160401b039180835f198201161819911816161590565b15612bc15790612b3b918193612b3686516001600160401b031690565b612a3e565b939015612bb55790612b6091612b5b60408501516001600160401b031690565b61278c565b80516001600160401b03838116911603612b7d575b505060019190565b612b93612bae9282906001600160401b03169052565b6119ca846001600160401b03165f52600260205260405f2090565b5f80612b75565b5050505090505f905f90565b506020830191612bd883516001600160401b031690565b612bf781836001600160401b039180835f198201161819911816161590565b156111c85785612c08928492612a3e565b949015612c645790612c2891612b5b60408601516001600160401b031690565b81516001600160401b03848116911603612c46575b50505060019190565b612c5c92612b9391906001600160401b03169052565b5f8080612c3d565b505050505090505f905f90565b50509050612ab8919250612c92612aa560408301516001600160401b031690565b516001600160401b03169056fea26469706673582212208cfcacc5e1a529ebf0fcdf9254e84504f7511e5db7c567bdad1b03249b26671764736f6c634300081c0033a2646970667358221220d6de167b9393a116407f94a7de5fd4b2dfbc1c6eeb2d7df3f323e0e632566f5b64736f6c634300081c0033
Deployed Bytecode Sourcemap
308:327:4:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;308:327:4;;;;;;-1:-1:-1;;;;;308:327:4;;;;;;;;583:13;;;;;;;;;;;;;308:327;583:13;;;;;;308:327;;583:13;;;308:327;583:13;;;;;308:327;;-1:-1:-1;;;;;308:327:4;;;;;;;;583:13;308:327;;;;;;;;;583:13;308:327;;;;;;;;;;;;;;
Swarm Source
ipfs://d6de167b9393a116407f94a7de5fd4b2dfbc1c6eeb2d7df3f323e0e632566f5b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.