ETH Price: $3,441.96 (-4.39%)
Gas: 28 Gwei

Contract

0x60e31A1a38213Ec3Ba1C7345EA49C8b57f7bA4D7
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Transfer From174926392023-06-16 13:17:11276 days ago1686921431IN
Cryptograph: Cryptograph Token
0 ETH0.0014905715.30227259
Set Approval For...170108982023-04-09 12:59:47344 days ago1681045187IN
Cryptograph: Cryptograph Token
0 ETH0.001319921.61692791
Set Approval For...158804872022-11-02 6:16:23502 days ago1667369783IN
Cryptograph: Cryptograph Token
0 ETH0.0006272310.27257536
Set Approval For...158254182022-10-25 13:32:23510 days ago1666704743IN
Cryptograph: Cryptograph Token
0 ETH0.0018922330.99020876
Set Approval For...146002102022-04-17 2:41:54701 days ago1650163314IN
Cryptograph: Cryptograph Token
0 ETH0.001370922.42122743
Set Approval For...144994802022-04-01 9:15:56717 days ago1648804556IN
Cryptograph: Cryptograph Token
0 ETH0.0026318543.04429504
Set Approval For...142528022022-02-22 1:05:27756 days ago1645491927IN
Cryptograph: Cryptograph Token
0 ETH0.0026184766.74517534
Set Approval For...139968152022-01-13 11:11:45795 days ago1642072305IN
Cryptograph: Cryptograph Token
0 ETH0.00969171158.50892865
Set Approval For...139858692022-01-11 18:21:17797 days ago1641925277IN
Cryptograph: Cryptograph Token
0 ETH0.00844383138.09980209
Set Approval For...138159932021-12-16 11:39:37823 days ago1639654777IN
Cryptograph: Cryptograph Token
0 ETH0.0026267242.96029506
Set Approval For...137983952021-12-13 18:05:13826 days ago1639418713IN
Cryptograph: Cryptograph Token
0 ETH0.00621369101.62556382
Set Approval For...137733482021-12-09 21:12:35830 days ago1639084355IN
Cryptograph: Cryptograph Token
0 ETH0.00674239110.27263718
Set Approval For...137531442021-12-06 15:39:55833 days ago1638805195IN
Cryptograph: Cryptograph Token
0 ETH0.00806285131.8688819
Set Approval For...133097662021-09-27 19:35:46903 days ago1632771346IN
Cryptograph: Cryptograph Token
0 ETH0.0051951784.96757253
Set Approval For...132438102021-09-17 14:46:58913 days ago1631890018IN
Cryptograph: Cryptograph Token
0 ETH0.0036992660.50189327
Set Approval For...131993952021-09-10 17:32:24920 days ago1631295144IN
Cryptograph: Cryptograph Token
0 ETH0.00871353142.5108286
Set Approval For...131815722021-09-07 23:27:51923 days ago1631057271IN
Cryptograph: Cryptograph Token
0 ETH0.005600391.59348266
Set Approval For...131223422021-08-29 19:50:54932 days ago1630266654IN
Cryptograph: Cryptograph Token
0 ETH0.0057709894.38500995
Set Approval For...131139942021-08-28 12:51:44933 days ago1630155104IN
Cryptograph: Cryptograph Token
0 ETH0.0033863955.38488764
Safe Transfer Fr...129861612021-08-08 18:45:29953 days ago1628448329IN
Cryptograph: Cryptograph Token
0.49725 ETH0.008241542.8034639
Set Approval For...129460452021-08-02 12:52:06959 days ago1627908726IN
Cryptograph: Cryptograph Token
0 ETH0.0016508627
Transfer From116210072021-01-09 14:07:511164 days ago1610201271IN
Cryptograph: Cryptograph Token
0 ETH0.0040550465.79770252
Transfer From116209722021-01-09 14:00:231164 days ago1610200823IN
Cryptograph: Cryptograph Token
0 ETH0.0040675166
Set Weth114207032020-12-09 19:59:581195 days ago1607543998IN
Cryptograph: Cryptograph Token
0 ETH0.0026951640
Transfer From111840252020-11-03 11:47:141231 days ago1604404034IN
Cryptograph: Cryptograph Token
0 ETH0.0008193327.5
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Txn Hash Block From To Value
129861612021-08-08 18:45:29953 days ago1628448329
Cryptograph: Cryptograph Token
0.49725 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ERC2665ProxiedV1

Compiler Version
v0.6.6+commit.6c089d02

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity Multiple files format)

File 21 of 40: ERC2665ProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./ERC2665V1.sol";

/// @author Guillaume Gonnaud 2020
/// @title  Cryptograph ERC2665 Mimic Smart Contract
/// @notice The proxied  ERC2665 Mimic : this is the contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract ERC2665ProxiedV1 is VCProxy, ERC2665HeaderV1, ERC2665StorageInternalV1 {

    constructor(uint256 _version, address _vc)public
    VCProxy(_version, _vc) //Calls the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }

    //No other logic code as it is all proxied

}

File 1 of 40: AuctionHouseEmptyV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./AuctionHouseV1.sol";

/// @author Guillaume Gonnaud 2018
/// @title Auction House Empty Logic Code
/// @notice Contain all the storage of the auction house declared in a way that generate getters for Logic Code use, but no code that changes memory
contract AuctionHouseEmptyV1 is VCProxyData, AuctionHouseHeaderV1, AuctionHouseStoragePublicV1 {

    //No functions, including no withdraw/catchall, in case of an exploit to the pending withdrawals array.
    //Any non "view" call will fail.

}

File 2 of 40: AuctionHouseLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./AuctionHouseV1.sol";

import "./CryptographIndexLogicV1.sol";
import "./TheCryptographLogicV1.sol";
import "./SingleAuctionLogicV1.sol";
import "./CryptographFactoryLogicV1.sol";
import "./MintingAuctionLogicV1.sol";
import "./ERC2665LogicV1.sol";
import "./CryptographKYCLogicV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title Auction House Logic Code
/// @notice The main contract used by collectors to Bid on and Trade cyptographs. Abstract this smart contract on the proxy address for interaction.
contract AuctionHouseLogicV1 is VCProxyData, AuctionHouseHeaderV1, AuctionHouseStoragePublicV1 {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall hence its memory state is irrelevant
    constructor() public
    {
        //Self intialize (nothing)
    }

    //Modifier for functions that require to be called only by the Cryptograph Factory
    modifier restrictedToFactory(){
        require((msg.sender == factory), "Only the Cryptograph Factory smart contract can call this function");
        _;
    }

    /// @notice Generic catch-all function that accept payments to prevent accidental Eth burn.
    /// @dev A receive function is NOT necessary. Your compiler doesn't know better. 
    fallback() external payable {
        pendingWithdrawals[msg.sender] += msg.value;
        emit Deposit(msg.value,msg.sender, msg.sender, msg.sender);
    }

    /// @notice You now have to pay real money to supress compiler warnings
    /// @dev Completely unecessary trashcode that will always get intercepted by the proxy first
    receive() external payable{
        pendingWithdrawals[msg.sender] += msg.value;
        emit Deposit(msg.value,msg.sender, msg.sender, msg.sender);
    }

    /// @notice Init function of the auction house
    /// @dev Callable only once after deployment
    /// @param _factory The address of the CryptographFactory Instance
    /// @param _index The address of the CryptographIndex Instance
    /// @param _ERC2665Lieutenant The address of the ERC2665 Instance
    /// @param _kycContract The address of the KYC contract Instance
    function init(address _factory, address _index, address _ERC2665Lieutenant, address _kycContract) external {
        require((initialized == false), "The Auction House has already been initialized");
        require(_factory != address(0), "_factory should be != 0x0");
        require(_index != address(0), "_index should be != 0x0");
        require(_ERC2665Lieutenant != address(0), "_ERC2665Lieutenant should be != 0x0");
        require(_kycContract != address(0), "_kycContract should be != 0x0");
        
        initialized = true;
        factory = _factory;
        index = _index;
        ERC2665Lieutenant = _ERC2665Lieutenant;
        kycContract = _kycContract;
    }

    /// @notice Send eth and add it to a withdrawal account.
    /// @dev This is how single auctions smart contracts deposit money
    /// @param _account The address of the account to credit with the payable amount
    /// @param _contributor The address of the user wallet from which the money is from
    function addFundsFor(address _account, address _contributor) external payable{
        pendingWithdrawals[_account] += msg.value;
        emit Deposit(msg.value, _account, _contributor, msg.sender);
    }

    /// @notice Withdraw all the eth from msg.sender account
    /// @dev Only way an external account can take away money from the cryptograph ecosystem
    function withdraw() external {

        //Calculating the withdrawn amount
        uint256 amount = pendingWithdrawals[msg.sender];

        //Emptying the account
        pendingWithdrawals[msg.sender] = 0;

        //Firing the event
        emit UserWithdrawal(amount, msg.sender);

        //Finally transferring the money
        msg.sender.transfer(amount);
    }

    /// @notice Place a bid to own a cryptograph
    /// @dev Calling this function is the only way to eventually gain ownership of a cryptograph
    /// @param _cryptographIssue The serial of the Cryptograph you want to bid on
    /// @param _isOfficial True if bidding on an official cryptograph, false if bidding on a community cryptograph
    /// @param _editionSerial If you are bidding on an edition, specify it's specific edition issue # here
    /// @param _newBidAmount The amount of money you want to bid
    /// @param _previousStandingBidAmount Protection against ordonancement attacks. Please indicate what value is currently visible for yourCryptographAuction.highestBidder().
    function bid(
        uint256 _cryptographIssue,
        bool _isOfficial,
        uint256 _editionSerial,
        uint256 _newBidAmount,
        uint256 _previousStandingBidAmount
    ) external payable{

        //KYC
        require(CryptographKYCLogicV1(kycContract).checkKyc(msg.sender, _newBidAmount),
            "Bid above a specific amount requires the bidder to be KYCed");

        //Grabbing the auction
        SingleAuctionLogicV1 _auc = SingleAuctionLogicV1(
            TheCryptographLogicV1(
                CryptographIndexLogicV1(index).getCryptograph(_cryptographIssue, _isOfficial, _editionSerial)
            ).myAuction()
        );

        //The first check being made is that the current highest standing bid match the announced bid the user is bidding on top of
        require(_auc.currentBids(_auc.highestBidder()) == _previousStandingBidAmount,
            "bid not accepted: current highest standing bid is different than the one specified");

        //Credit the full amount paid to the msg.sender account
        pendingWithdrawals[msg.sender] += msg.value;

        //Check that the sender has enough money in his account to bid
        require(pendingWithdrawals[msg.sender] + _auc.currentBids(msg.sender) >= _newBidAmount, "bid not accepted: Not enough ether was sent");

        uint256 toSend = _newBidAmount - _auc.currentBids(msg.sender);

        //Tapping the money from the account
        pendingWithdrawals[msg.sender] -= toSend;

        //Emiting the bidding event first (before the deposit events from payouts start triggering)
        emit UserBid(address(_auc), _newBidAmount, msg.sender);

        //Bidding
        _auc.bid{value: toSend }(_newBidAmount, msg.sender);
    }

    /// @notice Return the highest bid placed on a cryptograph
    /// @dev Easy way to optain the highest current bid for a cryptograph you want to bid on. Can be nested within bid() for unsafe bids that will always go through.
    /// @param _cryptographIssue The serial of the Cryptograph you want to peek highest bid on
    /// @param _isOfficial True if peeking on an official cryptograph, false if peeking on a community cryptograph
    /// @param _editionSerial If you are peeking on an edition, specify it's specific edition issue # here
    function getHighestBid(uint256 _cryptographIssue, bool _isOfficial, uint256 _editionSerial) external view returns(uint256){
        SingleAuctionLogicV1 _auc = SingleAuctionLogicV1(
            TheCryptographLogicV1(
                CryptographIndexLogicV1(index).getCryptograph(_cryptographIssue, _isOfficial, _editionSerial)
            ).myAuction()
        );
        return _auc.currentBids(_auc.highestBidder());
    }

    /// @notice Cancel your bid on a Cryptograph. WARNING : if highest bidder, you might only get a fraction of your money back
    /// @dev During the initial auction or once a sale has been accepted, a highest bidder can't cancel his bid.
    /// @param _cryptographIssue The serial of the Cryptograph you want to cancel bid on
    /// @param _isOfficial True if cancelling bid on an official cryptograph, false if cancelling bid on a community cryptograph
    /// @param _editionSerial If cancelling bid on an edition, specify it's specific edition issue # here
    function cancelBid(uint256 _cryptographIssue, bool _isOfficial, uint256 _editionSerial) external{

        //Grabbing the auction
        SingleAuctionLogicV1 _auc = SingleAuctionLogicV1(
            TheCryptographLogicV1(
                CryptographIndexLogicV1(index).getCryptograph(_cryptographIssue, _isOfficial, _editionSerial)
            ).myAuction()
        );

        //Emitting the cancelBid event before deposit event is triggered
        emit UserCancelledBid(address(_auc), msg.sender);

        //Actually cancel the bid
        _auc.cancelBid(msg.sender);
    }

    /// @notice Win a cryptograph for the highest bidder
    /// @dev Callable by anyone, but should be called by PA for automatisation.
    /// @param _cryptographIssue The issue # of the Cryptograph you want to claim
    /// @param _isOfficial True if claiming an official cryptograph, false if claiming a community cryptograph
    /// @param _editionSerial If claiming an edition, specify it's specific edition issue # here
    function win(uint256 _cryptographIssue, bool _isOfficial, uint256 _editionSerial) external{

        //Grabbing the auction
        TheCryptographLogicV1 _cry = TheCryptographLogicV1(
                CryptographIndexLogicV1(index).getCryptograph(_cryptographIssue, _isOfficial, _editionSerial)
            );

        SingleAuctionLogicV1 _auc = SingleAuctionLogicV1(_cry.myAuction());

        //Emitting the Win event before other events are triggered
        emit UserWin(address(_auc), _auc.currentBids(_auc.highestBidder()), _auc.highestBidder());

        //Update the ERC2665
        ERC2665LogicV1(ERC2665Lieutenant).transferACryptograph(_cry.owner(), _auc.highestBidder(), address(_cry), _auc.currentBids(_auc.highestBidder()));

        //Actually Win the auction and claim the cryptograph
        if(!(_auc.win(_auc.highestBidder()) == 0)){
            CryptographFactoryLogicV1(factory).mintGGBMA(_cryptographIssue, _isOfficial, _auc.highestBidder()); //Minting in the case of GGBMA
            MintingAuctionLogicV1(address(_auc)).distributeBid(_auc.highestBidder()); //Distributing the money
        }
    }

    /// @notice Set a selling price for a cryptograph if you are the owner. Set to 0 if not for sale, a sale is triggered if the selling price is leq than current highest bid.
    /// @dev Only callable by the Cryptograph owner
    /// @param _cryptographIssue The issue of the Cryptograph you want to sell
    /// @param _isOfficial True if selling an official cryptograph, false if selling a community cryptograph
    /// @param _editionSerial If selling an edition, specify its specific edition issue # here
    /// @param _newSellingPrice the new selling price you want to set
    function setSellingPrice(uint256 _cryptographIssue, bool _isOfficial, uint256 _editionSerial, uint256 _newSellingPrice) external{

        //Grabbing the auction
        SingleAuctionLogicV1 _auc = SingleAuctionLogicV1(
            TheCryptographLogicV1(
                CryptographIndexLogicV1(index).getCryptograph(_cryptographIssue, _isOfficial, _editionSerial)
            ).myAuction()
        );

        //Emitting the UserSell event before other events are triggered
        emit UserSellingPriceAdjust(address(_auc), _newSellingPrice);

        //Actually adjust the selling price
        _auc.setSellingPrice(msg.sender, _newSellingPrice);
    }

    /// @notice Call an ERC2665 transfer on a cryptograph
    /// @dev Only callable by the ERC2665 contract
    /// @param _cryptograph The address of the cryptograph getting transferred
    /// @param _contributor The address of the transfer fee payer
    /// @param _to The address of the new cryptograph owner
    function transferERC2665(address _cryptograph, address _contributor, address _to) external payable{
        require(msg.sender == ERC2665Lieutenant, "Only the ERC2665Lieutenant can call this function");
        SingleAuctionLogicV1(TheCryptographLogicV1(_cryptograph).myAuction()).transferERC2665{value:msg.value}(_contributor, _to);
    }

    /// @notice Call an ERC2665 Approve on a cryptograph
    /// @dev Only callable by the ERC2665 contract
    /// @param _cryptograph The address of the cryptograph getting transferred
    /// @param _contributor The address of the transfer fee payer
    /// @param _approvedAddress The address of the potential owner
    function approveERC2665(address _cryptograph, address _contributor, address _approvedAddress) external payable{
        require(msg.sender == ERC2665Lieutenant, "Only the ERC2665Lieutenant can call this function");
        SingleAuctionLogicV1(TheCryptographLogicV1(_cryptograph).myAuction()).approveERC2665{value:msg.value}(_contributor, _approvedAddress);
    }

}


File 3 of 40: AuctionHouseProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./AuctionHouseV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Auction House Proxy Smart Contract
/// @notice The proxied AuctionHouse : this is the contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract AuctionHouseProxiedV1 is VCProxy, AuctionHouseHeaderV1, AuctionHouseStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that this contract know where it's logic code is
    {
        //Self intialize (nothing)
    }
    //No other logic code it is all proxied
}




File 4 of 40: AuctionHouseV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Auction House Header
/// @notice Contain all the events emitted by the Auction House
contract AuctionHouseHeaderV1 {

    // Deposit: Event emitted whenever money is made available for withdrawal in the Auction House
    // amount: Amount of money being deposited
    // beneficiary: Account that will be able to withdraw the money
    // contributor: Which user wallet initially contributed the received money
    // origin: Which smart contract sent the money
    event Deposit(uint256 indexed amount, address indexed beneficiary, address indexed contributor, address origin);

    // Withdrawal: event emitted whenever a user withdraw his Eth on the auction house smart contract
    // amount: total amount of money withdrawn
    // account: address of user withdrawing his money
    event UserWithdrawal(uint256 indexed amount, address indexed account);

    // Bid: event emitted whenever a user submit a new bid to an auction
    // auction: the address of the auction
    // bidValue: the eth value of the new standing bid
    // bidder: the address of the user who just bid
    event UserBid(address indexed auction, uint256 indexed bidValue, address indexed bidder);

    // CancelBid: event emitted whenever a user manually cancel a bid
    // auction: the address of the auction
    // bidder: the address of the user who just cancelled his bid
    event UserCancelledBid(address indexed auction, address indexed bidder);

    // Win: event emitted whenever a user win an auction
    // auction: the address of the auction
    // bidValue: the eth value of the winning bid
    // bidder: the address of the user who just won the auction his bid
    event UserWin(address indexed auction, uint256 indexed bidValue, address indexed bidder);

    // UserSell: event emitted whenever a user trigger a sale at an auction
    // auction: the address of the auction
    event UserSell(address indexed auction);

    // UserSellingPriceAdjust: event emitted whenever a user adjust the selling price of an auction
    // auction: the address of the auction
    // value : the new adjusted price. 0 for disabled
    event UserSellingPriceAdjust(address indexed auction, uint256 indexed value);
}


/// @author Guillaume Gonnaud 2019
/// @title Auction House Storage Internal
/// @notice Contain all the storage of the auction house declared in a way that does not generate getters for Proxy use
contract AuctionHouseStorageInternalV1 {
    bool internal initialized; //Bool to check if the index have been initialized
    address internal factory; //The factory smart contract (proxy) that will publish the cryptographs
    address internal index; //The index smart contract that maps cryptographs and their auctions
    mapping (address => uint) internal pendingWithdrawals;  //How much money each user owns on the smart contract

    address internal ERC2665Lieutenant;
    address internal kycContract;
}


/// @author Guillaume Gonnaud
/// @title Auction House Storage Public
/// @notice Contain all the storage of the auction house declared in a way that generates getters for Logic Code use
contract AuctionHouseStoragePublicV1 {
    bool public initialized; //Bool to check if the index have been initialized
    address public factory; //The factory smart contract (proxy) that will publish the cryptographs
    address public index; //The index smart contract that maps cryptographs and their auctions
    mapping (address => uint) public pendingWithdrawals;  //How much money each user owns on the smart contract

    address public ERC2665Lieutenant;
    address public kycContract;

}

File 5 of 40: BidLink.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title BidLink Ordered Data Structure
/// @notice Classical double linked array data structure that allow us to not have to sort() stuff at the cost of instancing more + proper maintenance
contract BidLink{

    address public mintingAuction; //The Minting auction the BidLink is associated with
    address public bidder; //Our bidder
    uint256 public bidAmount; //How big is our bid
    address public above;  //BidLink with a bigger bid
    address public below;  //BidLink with a smaller bid

    modifier restrictedToAuction(){
        require((msg.sender == mintingAuction), "Only the auction contract can call this function");
        _;
    }

    constructor (address _bidder, uint256 _bidAmount) public
    {
        mintingAuction = msg.sender;
        bidder = _bidder;
        bidAmount = _bidAmount;
    }

    //Function used to reset and reuse a link rather than having to reinstance its bytecode
    function reset(address _bidder, uint256 _bidAmount) external restrictedToAuction(){
        delete above;
        delete below;
        bidder = _bidder;
        bidAmount = _bidAmount;
    }

    function setBidAmount(uint256 _bidAmount) external restrictedToAuction(){
        bidAmount = _bidAmount;
    }

    function setAbove(address _above) external restrictedToAuction(){
        above = _above;
    }

    function setBelow(address _below) external restrictedToAuction(){
        below = _below;
    }

}

File 6 of 40: BidLinkSimple.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title BidLink Ordered Data Structure (no bidAmount)
/// @notice Classical double linked array data structure that allow us to not have to sort() stuff at the cost of instancing more + proper maintenance
contract BidLinkSimple{

    address public auction; //The Minting auction the BidLink is associated with
    address public bidder; //Our bidder
    address public above;  //BidLink with a bigger bid
    address public below;  //BidLink with a smaller bid

    modifier restrictedToAuction(){
        require((msg.sender == auction), "Only the auction contract can call this function");
        _;
    }

    constructor (address _bidder) public
    {
        auction = msg.sender;
        bidder = _bidder;
    }

    //Function used to reset and reuse a link rather than having to reinstance its bytecode
    function reset(address _bidder) external restrictedToAuction(){
        delete above;
        delete below;
        bidder = _bidder;
    }

    function setAbove(address _above) external restrictedToAuction(){
        above = _above;
    }

    function setBelow(address _below) external restrictedToAuction(){
        below = _below;
    }

}

File 7 of 40: CryptographFactoryLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./CryptographFactoryV1.sol";
import "./TheCryptographProxiedV1.sol";
import "./TheCryptographLogicV1.sol";
import "./SingleAuctionProxiedV1.sol";
import "./SingleAuctionLogicV1.sol";
import "./CryptographIndexLogicV1.sol";
import "./AuctionHouseLogicV1.sol";
import "./MintingAuctionLogicV1.sol";
import "./MintingAuctionProxiedV1.sol";
import "./CryptographInitiator.sol";

/// @author Guillaume Gonnaud 2019
/// @title Cryptograph Factory Logic Code
/// @notice The main contract used by publisher to release and edit cryptographs. Cast this smart contract on the proxy address for interaction.
contract CryptographFactoryLogicV1 is VCProxyData, CryptographFactoryHeaderV1, CryptographFactoryStoragePublicV1 {

    constructor() public
    {
        //Self intialize (nothing)
    }

    /// @notice Init function of the Cryptograph Factory
    /// @dev Callable only once after deployment
    /// @param _officialPublisher The address that perpetual altruism will be using consistently through all deployments
    /// @param _targetVC The address of the proxied Version Control
    /// @param _targetAuctionHouse The address of the proxied Auction House
    /// @param _targetIndex The address of the proxied Index
    /// @param _targetCryLogicVersion The code index of TheCryptographLogicV1 in the version control
    /// @param _targetAuctionLogicVersion  The code index of SingleAuctionLogicV1 in the version control
    /// @param _targetAuctionBidLogicVersion  The code index of SingleAuctionBidLogicV1 in the version control
    /// @param _targetMintingAuctionLogicVersion  The code index of MintingAuctionLogicV1 in the version control
    function init(
        address _officialPublisher,
        address _targetVC,
        address _targetAuctionHouse,
        address _targetIndex,
        uint256 _targetCryLogicVersion,
        uint256 _targetAuctionLogicVersion,
        uint256 _targetAuctionBidLogicVersion,
        uint256 _targetMintingAuctionLogicVersion
    ) external {

        require(!initialized, "The Cryptograph Factory has already been initialized");
        initialized = true;
        officialPublisher = _officialPublisher;
        targetVC = _targetVC;
        targetAuctionHouse = _targetAuctionHouse;
        targetIndex = _targetIndex;
        targetCryLogicVersion = _targetCryLogicVersion;
        targetAuctionLogicVersion = _targetAuctionLogicVersion;
        targetAuctionBidLogicVersion = _targetAuctionBidLogicVersion;
        targetMintingAuctionLogicVersion = _targetMintingAuctionLogicVersion;
        communityMintable = false;
    }

    /// @notice Create a Cryptograph
    /// @dev emit CryptographCreated event. Official cryptographs need their auction locked-in afterwards.
    /// @param _cryInitiator The cryptograph Initiator object
    /// @return The issue number of the newly created Cryptograph
    function createCryptograph (address _cryInitiator) external returns (uint256){

        bool offi = msg.sender == officialPublisher;

        require(communityMintable || offi, "Community Cryptographs can't be created at the moment");

        //Instance the Cryptograph
        address newCryptographProxied;
        address newSingleAuctionProxiedV1;
        (newCryptographProxied, newSingleAuctionProxiedV1) = instanceCryptograph(_cryInitiator, offi);

        //Book the Cryptograph into the index and get the issue #, then init
        uint256 _issue;
        if(offi){
            //Inserting an official Cryptograph
            _issue = CryptographIndexLogicV1(targetIndex).insertACryptograph(newCryptographProxied);
        } else {
             //Inserting a community Cryptograph
            _issue = CryptographIndexLogicV1(targetIndex).insertACommunityCryptograph(newCryptographProxied);
        }

        TheCryptographLogicV1(newCryptographProxied).initCry(
                _issue, 0, offi, newSingleAuctionProxiedV1, _cryInitiator, address(0)
            );

        //Setting hash and url
        TheCryptographLogicV1(newCryptographProxied).setMediaHash(
            CryptographInitiator(_cryInitiator).mediaHash()
        );
        TheCryptographLogicV1(newCryptographProxied).setMediaUrl(
            CryptographInitiator(_cryInitiator).mediaUrl()
        );

        emit CryptographCreated(_issue, newCryptographProxied, offi);
        return _issue;
    }

    /// @notice Create an edition
    /// @dev emit CryptographEditionAdded event
    /// @param _editionSize How many cryptographs can be minted in this edition
    /// @return The Cryptograph issue # of the newly created Cryptograph Edition
    function createEdition(uint256 _editionSize) external returns (uint256){

        uint256 _issue;

        bool offi = msg.sender == officialPublisher;

        require(communityMintable || offi, "community Cryptographs can't be created at the moment");

        //Book the edition into the index and get the issue #
        if(offi){
            //Inserting an official Cryptograph
            _issue = CryptographIndexLogicV1(targetIndex).createAnEdition(msg.sender, _editionSize);
        } else {
            //Inserting a community Cryptograph
            _issue = CryptographIndexLogicV1(targetIndex).createACommunityEdition(msg.sender, _editionSize);
        }
        emit CryptographEditionAdded(_issue, _editionSize, offi);
        return _issue;
    }

    /// @notice Mint a Cryptograph that is part of an edition.
    /// @dev emit CryptographEditionMinted event. Only callable by the original edition creator. Official cryptographs need their auction locked-in afterwards.
    /// @param _cryInitiator The cryptograph Initiator for the edition
    /// @return The serial number of the newly created Cryptograph edition member
    function mintEdition (address _cryInitiator) external returns (uint256){

        bool offi = msg.sender == officialPublisher;

        uint256 _issue = CryptographInitiator(_cryInitiator).cryptographIssue();

        //Check that we are properly minting an Edition and not a GGBMA/Unique cryptograph
        require(
            CryptographIndexLogicV1(targetIndex).getCryptograph(_issue, offi, 0) == address(0x0),
            "Can't manually mint a GGBMA");

        //Instance the Cryptograph
        address newCryptographProxied;
        address newSingleAuctionProxiedV1;
        (newCryptographProxied, newSingleAuctionProxiedV1) = instanceCryptograph(_cryInitiator, offi);

        //Book the Cryptograph into the index and get the serial #
        uint256 _editionSerial;
        _editionSerial = CryptographIndexLogicV1(targetIndex).mintAnEdition(
            msg.sender,
            _issue,
            offi,
            address(newCryptographProxied)
        );

        //Init the Cryptograph
        TheCryptographLogicV1(address(newCryptographProxied)).initCry(
            _issue, _editionSerial, offi, address(newSingleAuctionProxiedV1), _cryInitiator, address(0)
        );
        emit CryptographEditionMinted(
            _issue,
            _editionSerial,
            newCryptographProxied,
            offi
        );

        //Setting hash and url
        TheCryptographLogicV1(newCryptographProxied).setMediaHash(
            CryptographInitiator(_cryInitiator).mediaHash()
        );
        TheCryptographLogicV1(newCryptographProxied).setMediaUrl(
            CryptographInitiator(_cryInitiator).mediaUrl()
        );

        return _editionSerial;
    }


    /// @notice ReInitialize an already created Cryptograph
    /// @dev This permit to release nameless cryptograph at a specific serial #, only to name them properly later (up until the auction start)
    /// If auction started but is not locked, use reinitAuction.
    /// @param _CryptographToEdit The address of the cryptograph you want to re-init
    /// @param _cryInitiator The Cryptograph initator with the name to be changed
    function reInitCryptograph(address _CryptographToEdit, address _cryInitiator)  external {
        require(msg.sender == officialPublisher, "Only official Cryptographs can be edited after serial # reservation");
        TheCryptographLogicV1(_CryptographToEdit).initCry(
            TheCryptographLogicV1(_CryptographToEdit).issue(),
            TheCryptographLogicV1(_CryptographToEdit).serial(),
            true,
            TheCryptographLogicV1(_CryptographToEdit).myAuction(),
            _cryInitiator,
            address(0)
        );
    }


    /// @notice ReInitialize an already created Auction. Not possible after locking.
    /// @dev Auction re-initializable until locked. No bid accepted if unlocked.
    /// @param _auctionToEdit The address of the auction you want to edit
    /// @param _cryInitiator The desired unix (seconds) timestamp at which the initial auction should start
    /// @param _lock Shall further re-initilization be allowed ?
    function reInitAuction(
        address _auctionToEdit,
        address _cryInitiator,
        bool _lock
    ) external {
        require(msg.sender == officialPublisher, "Only PA can reinit auctions");

        //Call init
        SingleAuctionLogicV1(_auctionToEdit).initAuction(
            SingleAuctionLogicV1(_auctionToEdit).myCryptograph(),
            _cryInitiator,
            _lock
        );
    }

    /// @notice Lock an auction to prevent anyone from re-editing it and allow bidding
    /// @dev When releasing ready-to launch cryptographs, you should lock ASAP
    /// @param _cryptographIssue The issue # of the Cryptograph auction you want to lock
    /// @param _editionSerial If locking auction on an edition, specify it's specific edition serial # here
    function lockAuction(uint256 _cryptographIssue, uint256 _editionSerial) external {
        require(msg.sender == officialPublisher, "Only Perpetual Altruism can lock an auction");
        SingleAuctionLogicV1(
            TheCryptographLogicV1(
                CryptographIndexLogicV1(targetIndex).getCryptograph(
                    _cryptographIssue, true, _editionSerial)
            ).myAuction()
        ).lock();
    }

    /// @notice Set the media hash for a cryptograph
    /// @dev emit the MediaHash event in the cryptograph instance for  web3 retrieval. It's best practice to call this function soon after cryptograph creation
    /// @param _cryptographIssue The issue # of the Cryptograph you want to set the media hash for
    /// @param _editionSerial If setting hash on an edition, specify its specific edition serial # here
    function setMediaHash(uint256 _cryptographIssue, uint256 _editionSerial, string calldata _mediaHash) external{
        TheCryptographLogicV1 _cry = TheCryptographLogicV1(CryptographIndexLogicV1(targetIndex).getCryptograph(
                    _cryptographIssue, true, _editionSerial)
            );
        require(msg.sender == SingleAuctionLogicV1(_cry.myAuction()).publisher(),
            "Only the publisher of a Cryptograph can edit its media hash"
        );

        _cry.setMediaHash(_mediaHash);
    }

    /// @notice Set the media url for a cryptograph
    /// @dev emit the MediaUrl event in the cryptograph instance for  web3 retrieval. It's best practice to call this function soon after cryptograph creation
    /// @param _cryptographIssue The issue # of the Cryptograph you want to set the media url for
    /// @param _editionSerial If setting url on an edition, specify its specific edition serial # here
    function setMediaUrl(uint256 _cryptographIssue, uint256 _editionSerial, string calldata _mediaUrl) external{
        TheCryptographLogicV1 _cry = TheCryptographLogicV1(CryptographIndexLogicV1(targetIndex).getCryptograph(
                    _cryptographIssue, true, _editionSerial)
            );
        require(msg.sender == SingleAuctionLogicV1(_cry.myAuction()).publisher(), "Only the publisher of a Cryptograph can edit its media URL");

        _cry.setMediaUrl(_mediaUrl);
    }

    /// @notice Instance a Cryptograph
    /// @dev The SingleAuction is init() but TheCryptograph is not
    /// @param _cryInitiator The Cryptograph Iniator address
    /// @return (new CryptographAddress, new SingleAuctionAddress)
    function instanceCryptograph( address _cryInitiator, bool _official) internal returns (address, address){

        //Instance a new Cryptograph
        TheCryptographProxiedV1 newCryptographProxied = new TheCryptographProxiedV1(targetCryLogicVersion, targetVC);

        //Instance a new auction
        SingleAuctionProxiedV1 newSingleAuctionProxiedV1 = new SingleAuctionProxiedV1(targetAuctionLogicVersion, targetVC, targetAuctionBidLogicVersion);

        //-----------------------
        //Init the auction
        SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).initAuction(
            address(newCryptographProxied),
            _cryInitiator,
            !_official //Will lock the auction setup if not an official Cryptograph
        );

        //-----------------------

        //Checking any bamboozling with the fees
        if(!_official){
                assert(SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).perpetualAltruismCut() >= 25000);
            }

        assert(
            SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).perpetualAltruismCut() +
            SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).publisherCut() +
            SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).charityCut() +
            SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).thirdPartyCut() == 100000
            );

        assert(SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).startTime() <=
        SingleAuctionLogicV1(address(newSingleAuctionProxiedV1)).endTime());

        return (address(newCryptographProxied), address(newSingleAuctionProxiedV1));

    }

    /*
    ====================================================
                    GENERALIZED GBM STUFF
    ====================================================
    */

    /*
        The processus for holding a Generalized GBM Auction (GGBMA) is as follow :
        -Call "createGGBMA" to create a generalized GBM auction
            -> It will have a cryptograph template that will be copied at minting time
            -> Edition serial is 0 (prototype) for get purposes.
            -> Reminder that once instanced, changes to the prototype don't carry over to copies.
        -"reInitCryptograph", "setMediaHash", "setMediaURL" works on the GGBMA prototype and copies just like a normal cryptograph
        -"reInitAuction", "lockAuction" allows to interact with a GGBMA just like a normal auction.
        -Once the auction is over, bidders calling win() on the auction serial #0 will instead mint a new cryptograph/Single auction pair
            -> This pair is initiated to be past their initial auction already and to have the bidder as the new owner.
    */

    /// @notice Create a Cryptograph
    /// @dev emit CryptographCreated event. Official cryptographs need their auction locked-in afterwards.
    /// @param _cryInitiator The cryptograph initiator for the desired GGBMA
    /// @return The serial number of the newly created Cryptograph
    function createGGBMA (address _cryInitiator) external returns (uint256){

        require(false, "GGBMA creation is disabled for launch, they will need an update approved by the senate");

        uint256 _issue; //The issue # we will get

        bool offi = msg.sender == officialPublisher;

        require(communityMintable || offi, "community Cryptographs can't be created at the moment");

        //Book the edition into the index and get the issue #
        if(offi){
            //Inserting an official Cryptograph
            _issue = CryptographIndexLogicV1(targetIndex).createAGGBMA(msg.sender, CryptographInitiator(_cryInitiator).maxSupply());
        } else {
            //Inserting a community Cryptograph
            _issue = CryptographIndexLogicV1(targetIndex).createACommunityGGBMA(msg.sender, CryptographInitiator(_cryInitiator).maxSupply());
        }
        emit CryptographEditionAdded(_issue, CryptographInitiator(_cryInitiator).maxSupply(), offi);

        //Instance the Cryptograph and the auction
        address newCryptographProxied;
        address newMintingAuctionProxiedV1;
        (newCryptographProxied, newMintingAuctionProxiedV1) = instanceCryptographGGBMA(_cryInitiator, _issue);

        //Book the prototype at index0 of the edition

        CryptographIndexLogicV1(targetIndex).mintAnEditionAt(
            _issue,
            0,
            offi,
            address(newCryptographProxied)
        );

        emit CryptographCreated(_issue, newCryptographProxied, offi);
        mintingAuctionSupply[newMintingAuctionProxiedV1] = CryptographInitiator(_cryInitiator).maxSupply();

        return _issue;
    }

    /// @notice Instance a Cryptograph/Minting Auction pair
    /// @dev The MintingAuction is init() but TheCryptograph is not
    /// @param _cryInitiator The cryptograph Initator the GGBMA will be created after
    /// @return (new CryptographAddress, new MintingAuctionAddress)
    function instanceCryptographGGBMA(address _cryInitiator, uint256 _issue) internal returns (address, address){


        //Is the GGBMA published by PA or a third paty ?
        bool _official; //Set to false by default
        if(msg.sender == officialPublisher){
            _official = true;
        }

        require(communityMintable || _official, "community Cryptographs can't be created at the moment");

        //Instance a new Cryptograph
        address newCryptographProxied = address(new TheCryptographProxiedV1(targetCryLogicVersion, targetVC));

        //Instance a new auction
        address newMintingAuctionProxiedV1 = address(new MintingAuctionProxiedV1(targetMintingAuctionLogicVersion, targetVC));

        //-----------------------

        TheCryptographLogicV1(address(newCryptographProxied)).initCry(
                _issue, 0, _official, newMintingAuctionProxiedV1, _cryInitiator, address(0)
            );

        //Init the auction
        MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).initAuction(
            newCryptographProxied,
            _cryInitiator,
            !_official //Will lock the auction setup if not an official Cryptograph
        );
        //-----------------------

        //Setting hash and url
        TheCryptographLogicV1(newCryptographProxied).setMediaHash(
            CryptographInitiator(_cryInitiator).mediaHash()
        );
        TheCryptographLogicV1(newCryptographProxied).setMediaUrl(
            CryptographInitiator(_cryInitiator).mediaUrl()
        );

        //Checking any bamboozling with the fees
        if(!_official){
                assert(MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).perpetualAltruismCut() >= 25000);
            }

        assert(
            MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).perpetualAltruismCut() +
            MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).publisherCut() +
            MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).charityCut() +
            MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).thirdPartyCut() == 100000
            );

        assert(MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).startTime() <=
            MintingAuctionLogicV1(address(newMintingAuctionProxiedV1)).endTime());


        return (address(newCryptographProxied), address(newMintingAuctionProxiedV1));
    }

    /// @notice Mint a Cryptograph/SA pair initialized to a GGBMA winner
    /// @dev To be called BEFORE the bid funds are distributed to the publisher
    /// @param _issue The issue number of the GGBMA
    /// @param _isOfficial Is it an official or community GGBMA ?
    /// @param _winner The address of someone eligible to win the auction
    function mintGGBMA(uint256 _issue, bool _isOfficial, address _winner) external returns(bool){
        require(msg.sender == targetAuctionHouse, "Only the auction house can ask the factory to mint new copies for a GGBMA");

        //Grabbing the GGBMA
        address _ggbma = TheCryptographLogicV1(
                CryptographIndexLogicV1(targetIndex).getCryptograph(_issue, _isOfficial, 0)
            ).myAuction();

        //Calculating the claimant ranking
        uint256 positionInAuction; //0

        //Browse the BidLink chain until the link above us has a bid greater or equal to us
        address currentLink = MintingAuctionLogicV1(_ggbma).bidLinks(MintingAuctionLogicV1(_ggbma).highestBidder());
        bool stop = currentLink == address(0x0); //Do not even enter the loop if there is no highest bidder
        while(!stop){
            if(BidLink(currentLink).bidder() == _winner){
                positionInAuction++; //Increasing the count (serial # start at 1 while counter start at 0)
                stop = true;
            } else if(BidLink(currentLink).below() == address(0x0)){ //Checking if we have reached the bottom
                positionInAuction = 0; //We were not a bidder...
                stop = true;
            } else {
                //Going down
                positionInAuction++;
                currentLink = BidLink(currentLink).below();
            }
        }

        require(positionInAuction != 0, "Could not find your bid in this auction");

        //Checking if we can mint. No refunds as those are handled by the auction itself.
        //The way we check for minting available is by checking if there is a standing bid.
        require(MintingAuctionLogicV1(_ggbma).currentBids(_winner) != 0, "You already minted your cryptograph");

        /*
        Double entry attack possible here on third parties minted cryptographs, as initiators are user instanced.

        Limitation of exploit :
        -only "views" are called (so no state-changing gas stealing)
        -only on community GGBMA

        Consequence at worst inside cryptograph ecosystem : The attacker (who is the original creator of the GGBMA)
        can make changes to each newly minted cryptograph (so that they are not all unique).

        => Not a bug. It's a feature.

        */

        //Instance the Cryptograph
        address newCryptographProxied;
        address newSingleAuctionProxiedV1;
        address initiator = MintingAuctionLogicV1(_ggbma).initiator();

        (newCryptographProxied, newSingleAuctionProxiedV1) = instanceCryptograph(initiator, _isOfficial);

        //Book the Cryptograph into the index
        CryptographIndexLogicV1(targetIndex).mintAnEditionAt(
            _issue, // Issue #
            positionInAuction, // Serial #
            _isOfficial,
            address(newCryptographProxied)
        );

        //Init the cryptograph
        TheCryptographLogicV1(newCryptographProxied).initCry(
                _issue, positionInAuction, _isOfficial, newSingleAuctionProxiedV1, initiator, _winner
            );

        //Setting hash and url
        TheCryptographLogicV1(newCryptographProxied).setMediaHash(
            CryptographInitiator(initiator).mediaHash()
        );
        TheCryptographLogicV1(newCryptographProxied).setMediaUrl(
            CryptographInitiator(initiator).mediaUrl()
        );

        //Locking the auction
        SingleAuctionLogicV1(newSingleAuctionProxiedV1).lock();
    }

    /// @notice Set the ability for third parties to create their own cryptographs.
    /// @dev False at creation
    /// @param _communityMintable Are community Cryptographs mintable ?
    function setCommunityMintable(bool _communityMintable) external {

        require(msg.sender == officialPublisher, "Only Perpetual Altruism can set communityMintable");

        communityMintable = _communityMintable;
    }
}

File 8 of 40: CryptographFactoryProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./CryptographFactoryV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Cryptograph Factory Proxy Smart Contract
/// @notice The proxied Factory : this is this contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract CryptographFactoryProxiedV1 is VCProxy, CryptographFactoryHeaderV1, CryptographFactoryStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }

    //No other logic code as it is all proxied

}




File 9 of 40: CryptographFactoryV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Factory Header
/// @notice Contain all the events emitted by the factory
contract CryptographFactoryHeaderV1 {
    event CryptographCreated(uint256 indexed cryptographIssue, address indexed cryptographAddress, bool indexed official);
    event CryptographEditionAdded(uint256 indexed cryptographIssue, uint256 indexed editionSize, bool indexed official);
    event CryptographEditionMinted(uint256 indexed cryptographIssue, uint256 indexed editionIssue, address cryptographAddress, bool indexed official);
}


/// @author Guillaume Gonnaud 2019
/// @title Factory Storage Internal
/// @notice Contain all the storage of the Factory declared in a way that does not generate getters for Proxy use
contract CryptographFactoryStorageInternalV1 {

    bool internal initialized; //A bool controlling if we have been initialized or not

    address internal officialPublisher; //The address that is allowed to publish the official (i.e. non-community) cryptographs

    /*
    ==================================================
                    Linking section
    ==================================================
    Those are the addresses of other smart contracts in the ecosystem and relevant value to them
    */
    address internal targetVC; //Address of the version control that the Cryptograph should use (potentially different than ours)
    address internal targetAuctionHouse; //Address of the Auction house used by Cryptograph
    address internal targetIndex; //Address of the Cryptograph library storing both fan made and public cryptographs

    // DO NOT PUT THE CRYPTOGRAPH PROXY CODE ADDRESS IN HERE, it needs to be in the logic code of the factory
    // IDEM FOR SINGLE AUCTION PROXY CODE
    uint256 internal targetCryLogicVersion; //Which version of the logic code in the Version Control array the cryptographs should use
    //Which version of the logic code in the Version Control array the Single Auction should use
    uint256 internal targetAuctionLogicVersion;
    //Which version of the logic code in the Version Control array the Single Auction Bid should use
    uint256 internal targetAuctionBidLogicVersion;
    //Which version of the logic code in the Version Control array the Minting Auction should use
    uint256 internal targetMintingAuctionLogicVersion;

    //Actual data storage section
    mapping (address => uint256) internal mintingAuctionSupply; //How much token can be created by each MintingAuction

    //Are Community cryptographs allowed to be minted ?
    bool internal communityMintable;

}


/// @author Guillaume Gonnaud 2019
/// @title Factory Storage Public
/// @notice Contain all the storage of the Factory declared in a way that generates getters for Logic use
contract CryptographFactoryStoragePublicV1 {

    bool public initialized; //A bool controlling if we have been initialized or not

    address public officialPublisher; //The address that is allowed to publish the non-community cryptographs

    /*
    ==================================================
                    Linking section
    ==================================================
    Those are the addresses of other smart contracts in the ecosystem and the relevant Version Control index value to them
    */
    address public targetVC; //Address of the version control the cryptographs should use
    address public targetAuctionHouse; //Address of the Auction house used by cryptograph
    address public targetIndex; //Address of the Cryptograph library storing both fan made and public cryptographs

    uint256 public targetCryLogicVersion; //Which version of the logic code in the Version Control array the cryptographs should use
    uint256 public targetAuctionLogicVersion; //Which version of the logic code in the Version Control array the Single Auction should use
    //Which version of the logic code in the Version Control array the Single Auction Bid should use
    uint256 public targetAuctionBidLogicVersion;
    //Which version of the logic code in the Version Control array the Minting Auction should use
    uint256 public targetMintingAuctionLogicVersion;

    //Actual data storage section
    mapping (address => uint256) public mintingAuctionSupply; //How much token can be created by each MintingAuction

    //Are Community cryptographs allowed to be minted ?
    bool public communityMintable;
}

File 10 of 40: CryptographIndexLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./CryptographIndexV1.sol";
import "./EditionIndexerProxiedV1.sol";
import "./EditionIndexerLogicV1.sol";
import "./ERC2665LogicV1.sol";

///@title Cryptograph Index Logic Contract
///@author Guillaume Gonnaud
///@notice Provide the logic code related to remembering the address of all the published cryptographs. Cast this contract on the proxy.
///@dev This contract and its functions should be called by the relevant proxy smart contract only
contract CryptographIndexLogicV1 is VCProxyData, CryptographIndexHeaderV1, CryptographIndexStoragePublicV1  {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence it's memory state is irrelevant
    constructor() public
    {
        //Self intialize (nothing)
    }

    //Modifier for functions that requires to be called only by the cryptograph factory
    modifier restrictedToFactory(){
        require((msg.sender == factory), "Only the cryptograph factory smart contract can call this function");
        _;
    }

    /// @notice Init function of the Index
    /// @dev Callable only once after deployment
    /// @param _factory The address of the CryptographFactory Instance
    /// @param _indexerLogicCodeIndex The index in the VC of editionIndexers logic code
    /// @param _ERC2665Lieutenant The address of the ERC721 Instance
    function init(address _factory, uint256 _indexerLogicCodeIndex, address _ERC2665Lieutenant) external returns(bool){
        require(!initialized, "The cryptograph index has already been initialized");
        factory = _factory;
        indexerLogicCodeIndex = _indexerLogicCodeIndex;
        initialized = true;
        cryptographs.push(address(0x0));
        communityCryptographs.push(address(0x0));
        ERC2665Lieutenant = _ERC2665Lieutenant;
        return true;
    }


    /// @notice Insert a cryptograph in the array and return the new index position
    /// @dev Only callable by Factory
    /// @param _cryptograph The address of the cryptograph to insert in the index
    /// @return (uint) The new index position in the cryptograph array
    function insertACryptograph(address _cryptograph) external restrictedToFactory() returns(uint){

        //Update the ERC2665
        ERC2665LogicV1(ERC2665Lieutenant).MintACryptograph(_cryptograph);
        cryptographs.push(_cryptograph);
        return (cryptographs.length - 1); //Inserting the cryptograph and returning the position in the array
    }


    /// @notice Insert a community cryptograph in the array and return the new index position
    /// @dev Only callable by Factory
    /// @param _communityCryptograph The address of the community cryptograph to insert in the index
    /// @return (uint) The new index position in the community cryptograph array
    function insertACommunityCryptograph(address _communityCryptograph) external restrictedToFactory() returns(uint){

        //Update the ERC2665
        ERC2665LogicV1(ERC2665Lieutenant).MintACryptograph(_communityCryptograph);

        communityCryptographs.push(_communityCryptograph);
        return (communityCryptographs.length - 1); //Inserting the community cryptograph and returning new position in array
    }


    /// @notice Create a new cryptograph edition and return the new index position
    /// @dev Only callable by Factory
    /// @param _minter The address of the user wallet that will have the responsability to mint all the editions
    /// @param _editionSize The maximum number of cryptograph that can be minted in the edition
    /// @return (uint) The new index position in the cryptograph array
    function createAnEdition(address _minter, uint256 _editionSize) external restrictedToFactory() returns(uint){
        require(_minter != address(0) && _editionSize != 0,
            "Minter address and edition size must be greater than 0"
        );

        //Create a new indexer for the edition
        EditionIndexerProxiedV1 _proxied = new EditionIndexerProxiedV1(indexerLogicCodeIndex, vc);

        //Initializing the indexer
        EditionIndexerLogicV1(address(_proxied)).init(address(this), _minter, _editionSize);

        //Adding the indexer to the mapping
        editionSizes[address(_proxied)] = _editionSize;

        //Indicate our type as edition
        cryptographType[address(_proxied)] = 1;

        //Inserting the edition and returning the position in the array
        cryptographs.push(address(_proxied));
        return (cryptographs.length - 1);
    }


    /// @notice Create a new cryptograph edition starting at 0 and return the new index position
    /// @dev Only callable by Factory
    /// @param _minter The address of the user wallet that will have the responsability to mint all the editions
    /// @param _editionSize The maximum number of cryptograph that can be minted in the edition
    /// @return (uint) The new index position in the cryptograph array
    function createAGGBMA(address _minter, uint256 _editionSize) external restrictedToFactory() returns(uint){
        require(_minter != address(0) && _editionSize != 0,
            "Minter address and edition size must be greater than 0"
        );

        //Create a new indexer for the edition
        EditionIndexerProxiedV1 _proxied = new EditionIndexerProxiedV1(indexerLogicCodeIndex, vc);

        //Initializing the indexer
        EditionIndexerLogicV1(address(_proxied)).init0(address(this), _minter, _editionSize+1);

        //Adding the indexer to the mapping
        editionSizes[address(_proxied)] = _editionSize;

        //Indicate our type as edition
        cryptographType[address(_proxied)] = 1;

        //Inserting the edition and returning the position in the array
        cryptographs.push(address(_proxied));
        return (cryptographs.length - 1);
    }


    /// @notice Create a new community edition and return the new index position
    /// @dev Only callable by Factory
    /// @param _minter The address of the user wallet that will have the responsability to mint all the editions
    /// @param _editionSize The maximum number of community cryptograph that can be minted in the edition
    /// @return (uint) The new index position in the cryptograph community array
    function createACommunityEdition(address _minter, uint256 _editionSize) external restrictedToFactory() returns(uint){
        //Create a new indexer for the edition
        EditionIndexerProxiedV1 _proxied = new EditionIndexerProxiedV1(indexerLogicCodeIndex, vc);

        //Initializing the indexer
        EditionIndexerLogicV1(address(_proxied)).init(address(this), _minter, _editionSize);

        //Adding the indexer to the mapping
        editionSizes[address(_proxied)] = _editionSize;

        //Indicate our type as edition
        cryptographType[address(_proxied)] = 1;

        //Inserting the edition and returning the position in the array
        communityCryptographs.push(address(_proxied));
        return (communityCryptographs.length - 1);
    }


    /// @notice Create a new cryptograph community edition starting at 0 and return the new index position
    /// @dev Only callable by Factory
    /// @param _minter The address of the user wallet that will have the responsability to mint all the editions
    /// @param _editionSize The maximum number of cryptograph that can be minted in the edition
    /// @return (uint) The new index position in the community cryptograph array
    function createACommunityGGBMA(address _minter, uint256 _editionSize) external restrictedToFactory() returns(uint){
        //Create a new indexer for the edition
        EditionIndexerProxiedV1 _proxied = new EditionIndexerProxiedV1(indexerLogicCodeIndex, vc);

        //Initializing the indexer
        EditionIndexerLogicV1(address(_proxied)).init0(address(this), _minter, _editionSize+1); //One more for the prototype

        //Adding the indexer to the mapping
        editionSizes[address(_proxied)] = _editionSize;

        //Indicate our type as edition
        cryptographType[address(_proxied)] = 1;

        //Inserting the edition and returning the position in the array
        communityCryptographs.push(address(_proxied));
        return (communityCryptographs.length - 1);
    }


    /// @notice Mint an Edition Cryptograph
    /// @dev Only callable by Factory
    /// @param _minter The address of the user wallet that is minting the cryptograph
    /// @param _cryptographIssue The issue # of the edition we are minting a new member of
    /// @param _isOfficial Is it a community edition or not ?
    /// @param _cryptograph The address of the cryptograph we are inserting in the edition indexer
    /// @return (uint) The serial of the newly inserted cryptograph
    function mintAnEdition(
        address _minter,
        uint256 _cryptographIssue,
        bool _isOfficial,
        address _cryptograph
    ) external restrictedToFactory() returns(uint){


        //Update the ERC2665
        ERC2665LogicV1(ERC2665Lieutenant).MintACryptograph(_cryptograph);

        //Indicate our type as edition
        cryptographType[_cryptograph] = 1;

        if(_isOfficial){
            uint256 edIdx = EditionIndexerLogicV1(cryptographs[_cryptographIssue]).insertACryptograph(_cryptograph, _minter);
            return edIdx;
        } else {
            uint256 edIdx = EditionIndexerLogicV1(communityCryptographs[_cryptographIssue]).insertACryptograph(_cryptograph, _minter);
            return edIdx;
        }
    }


    /// @notice Mint an Edition Cryptograph with a specific serial
    /// @dev Only callable by Factory
    /// @param _cryptographIssue The issue # of the edition we are minting a new member of
    /// @param _cryptographSerial The serial # we want to insert in the edition
    /// @param _isOfficial Is it a community edition or not ?
    /// @param _cryptograph The address of the cryptograph we are inserting in the edition indexer
    function mintAnEditionAt(
        uint256 _cryptographIssue,
        uint256 _cryptographSerial,
        bool _isOfficial,
        address _cryptograph
    ) external restrictedToFactory(){


        //Update the ERC2665
        ERC2665LogicV1(ERC2665Lieutenant).MintACryptograph(_cryptograph);

        //Indicate our type as edition
        cryptographType[_cryptograph] = 1;

        if(_isOfficial){
            EditionIndexerLogicV1(cryptographs[_cryptographIssue]).insertACryptographAt(_cryptograph, _cryptographSerial);
        } else {
            EditionIndexerLogicV1(communityCryptographs[_cryptographIssue]).insertACryptographAt(_cryptograph, _cryptographSerial);
        }
    }


    /// @notice Return the address of a Cryptograph using it's parameters
    /// @dev You can then cast this address as a Cryptograph to recover the single auction associated
    /// @param _cryptographIssue The issue # of the Cryptograph
    /// @param _isOfficial True if official Cryptograph, false if community Cryptograph
    /// @param _editionSerial The edition serial # of the Cryptograph. Ignored if the Cryptograph is not an edition
    /// @return The address of the grabbed cryptograph
    function getCryptograph(uint256 _cryptographIssue, bool _isOfficial, uint256 _editionSerial) external view returns(address){
        if(_isOfficial){
            if(cryptographType[address(cryptographs[_cryptographIssue])] == 0){
                //We are unique
                return(address(cryptographs[_cryptographIssue]));
            } else {
                //We are an edition/GGBMA
                return(address(EditionIndexerLogicV1(cryptographs[_cryptographIssue]).cryptographs(_editionSerial)));
            }
        } else {
            if(cryptographType[address(communityCryptographs[_cryptographIssue])] == 0){
               //We are unique
                return(address(communityCryptographs[_cryptographIssue]));
            } else {
                //We are an edition/GGBMA
                return(address(EditionIndexerLogicV1(communityCryptographs[_cryptographIssue]).cryptographs(_editionSerial)));
            }
        }
    }

}


File 11 of 40: CryptographIndexProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./CryptographIndexV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Cryptograph Index Proxy Smart Contract
/// @notice The proxied Index : this is this contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract CryptographIndexProxiedV1 is VCProxy, CryptographIndexHeaderV1, CryptographIndexStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }

    //No other logic code as it is all proxied

}




File 12 of 40: CryptographIndexV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Cryptoraph Indexer Header
/// @notice Contain all the events emitted by the Cryptoraph Indexer
contract CryptographIndexHeaderV1 {
}

/// @author Guillaume Gonnaud 2019
/// @title Cryptograph Indexer Storage Internal
/// @notice Contain all the storage of the Cryptograph Indexer declared in a way that don't generate getters for Proxy use
contract CryptographIndexStorageInternalV1 {
    bool internal initialized; //Bool to check if the index has been initialized
    address internal factory; //The factory smart contract (proxy) that will publish the cryptographs
    address[] internal cryptographs;
    address[] internal communityCryptographs;
    mapping (address => uint) internal editionSizes; //Set to 0 if unique (not edition)
    mapping (address => uint) internal cryptographType; //0 = Unique, 1 = Edition, 2 = Minting
    uint256 internal indexerLogicCodeIndex; //The index in the Version Control of the logic code

    address internal ERC2665Lieutenant;
}

/// @author Guillaume Gonnaud 2019
/// @title Cryptograph Indexer Storage Public
/// @notice Contain all the storage of the Cryptograph Indexer declared in a way that generates getters for logic use
contract CryptographIndexStoragePublicV1 {
    bool public initialized; //Bool to check if the index has been initialized
    address public factory; //The factory smart contract (proxy) that will publish the cryptographs
    address[] public cryptographs;
    address[] public communityCryptographs;
    mapping (address => uint) public editionSizes; //Set to 0 if unique (not edition)
    mapping (address => uint) public cryptographType; //0 = Unique, 1 = Edition, 2 = Minting
    uint256 public indexerLogicCodeIndex; //The index in the VC of the logic code

    address public ERC2665Lieutenant;
}


File 13 of 40: CryptographInitiator.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud
/// @title Cryptograph Initiator
/// @notice Basically a solidity bean so that we can pass it as argument without hitting stack too deep errors when creating a cryptograph
contract CryptographInitiator{
    address public owner; // The desired owner of the Cryptograph
    string public name; // The desired name of the Cryptograph
    string public creator; // The desired creatpr of the Cryptograph
    uint256 public auctionStartTime; //The desired unix (seconds) timestamp at which the initial auction should start
    uint256 public auctionSecondsDuration; // The duration in seconds of the initial auction
    address public publisher; // The address of the publisher. Can edit media url and hash for a cryptograph.
    uint256 public publisherCut; // How much out of 100k parts of profits should the publisher get. e.g. publisherCut = 25000 means 1/4
    address public charity; // The address of the charity
    uint256 public charityCut; // The charity cut out of 100k
    address public thirdParty; // The address of a third party
    uint256 public thirdPartyCut; // The third party cut out of 100k
    uint256 public perpetualAltruismCut; // Will always be set to 25k except very special occasions.
    uint256 public maxSupply; // How many of these cryptographs should be minted maximum
    uint256 public startingPrice; // The Starting price of the auction
    uint256 public cryptographIssue; // The desired issue of the cryptograph (only for editions)
    string public mediaHash; // The desired media hash of the cryptograph
    string public mediaUrl; // The desired media url of the cryptograph

    /// @param _name The desired name of the Cryptograph
    /// @param _auctionStartTime The desired unix (seconds) timestamp at which the initial auction should start
    /// @param _auctionSecondsDuration The duration in seconds of the initial auction
    /// @param _publisher The address of the publisher. Can edit media url and hash for a cryptograph.
    /// @param _publisherCut How much out of 100k parts of profits should the publisher get. e.g. _publisherCut = 25000 mean 1/4 of all profits
    /// @param _charity The address of the charity
    /// @param _charityCut The charity cut out of 100k
    /// @param _thirdParty The address of a third party
    /// @param _thirdPartyCut The third party cut out of 100k
    /// @param _perpetualAltruismCut Will always be set to 25k except very special occasions.
    /// @param _maxSupply How many of these cryptographs should be minted maximum
    /// @param _startingPrice The Starting price of the auction
    /// @param _cryptographIssue The desired issue of the cryptograph (only for editions)
    constructor (
                string memory _name,
                uint256 _auctionStartTime,
                uint256 _auctionSecondsDuration,
                address _publisher,
                uint256 _publisherCut,
                address _charity,
                uint256 _charityCut,
                address _thirdParty,
                uint256 _thirdPartyCut,
                uint256 _perpetualAltruismCut,
                uint256 _maxSupply,
                uint256 _startingPrice,
                uint256 _cryptographIssue
    ) public{
        owner = msg.sender;
        name = _name;
        auctionStartTime = _auctionStartTime;
        auctionSecondsDuration = _auctionSecondsDuration;
        publisher = _publisher;
        publisherCut = _publisherCut;
        charity = _charity;
        charityCut = _charityCut;
        thirdParty = _thirdParty;
        thirdPartyCut = _thirdPartyCut;
        perpetualAltruismCut = _perpetualAltruismCut;
        maxSupply = _maxSupply;
        startingPrice = _startingPrice;
        cryptographIssue = _cryptographIssue;

    }

    modifier restrictedToOwner(){
        require((msg.sender == owner), "Only the creator of this Contract can modify its memory");
        _;
    }

    function setName(string calldata _name) external restrictedToOwner(){
        name = _name;
    }

    function setAuctionStartTime(uint256 _auctionStartTime) external restrictedToOwner(){
        auctionStartTime = _auctionStartTime;
    }

    function setAuctionSecondsDuration(uint256 _auctionSecondsDuration) external restrictedToOwner(){
        auctionSecondsDuration = _auctionSecondsDuration;
    }

    function setPublisher(address _publisher) external restrictedToOwner(){
        publisher = _publisher;
    }

    function setPublisherCut(uint256 _publisherCut) external restrictedToOwner(){
        publisherCut = _publisherCut;
    }

    function setCharity(address _charity) external restrictedToOwner(){
        charity = _charity;
    }

    function setCharityCut(uint256 _charityCut) external restrictedToOwner(){
        charityCut = _charityCut;
    }

    function setThirdParty(address _thirdParty) external restrictedToOwner(){
        thirdParty = _thirdParty;
    }

    function setThirdPartyCut(uint256 _thirdPartyCut) external restrictedToOwner(){
        thirdPartyCut = _thirdPartyCut;
    }

    function setPerpetualAltruismCut(uint256 _perpetualAltruismCut) external restrictedToOwner(){
        perpetualAltruismCut = _perpetualAltruismCut;
    }

    function setMaxSupply(uint256 _maxSupply) external restrictedToOwner(){
        maxSupply = _maxSupply;
    }

    function setStartingPrice(uint256 _startingPrice) external restrictedToOwner(){
        startingPrice = _startingPrice;
    }

    function setCryptographIssue(uint256 _cryptographIssue) external restrictedToOwner(){
        cryptographIssue = _cryptographIssue;
    }

    function setMediaHash(string calldata _mediahash) external restrictedToOwner(){
        mediaHash = _mediahash;
    }

    function setMediaUrl(string calldata _mediaUrl) external restrictedToOwner(){
        mediaUrl = _mediaUrl;
    }

    function setCreator(string calldata _creator) external restrictedToOwner(){
        creator = _creator;
    }

}

File 14 of 40: CryptographKYCLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./CryptographKYCV1.sol";

/// @title Cryptograph KYC Logic Contract
/// @author Guillaume Gonnaud 2020
/// @notice Provides the logic code for the KYC of bidders
/// @dev Price feed is in ETH and NOT an oracle because it's a KYC price feed (We have to verify transaction above a certain GBP amount)
contract CryptographKYCLogicV1 is VCProxyData, CryptographKYCHeaderV1, CryptographKYCStoragePublicV1  {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor() public
    {
        //Self intialize (nothing)
    }

    modifier restrictedToOperators(){
        require((msg.sender == perpetualAltruism || authorizedOperators[msg.sender]), "Only operators can call this function");
        _;
    }

    /// @notice Init function of the KYC contract
    /// @dev Callable only once after deployment
    function init() external {
        require(perpetualAltruism == address(0), "Already initalized");
        perpetualAltruism = msg.sender;
        priceLimit = uint256(0) - uint256(1);
        emit PriceLimit(priceLimit);
    }

    /// @notice Used to allow other wallets to manage the KYC
    /// @dev Only callable by Perpetual Altruism/Other operators
    /// @param _operator The address of the operator
    /// @param _operating If the operator is allowed to operate
    function setOperator(address _operator, bool _operating) external restrictedToOperators(){
        authorizedOperators[_operator] = _operating;
    }


    /// @notice Used to set a price limit above which wallets need to be KYCed
    /// @dev Only callable by Perpetual Altruism/Other operators
    /// @param _newPrice The new price limit
    function setPriceLimit(uint256 _newPrice) external restrictedToOperators(){
        priceLimit = _newPrice;
        emit PriceLimit(_newPrice);
    }

    /// @notice Used to allow other wallets to manage the KYC
    /// @dev Only callable by Perpetual Altruism/Other operators
    /// @param _user The address of the user
    /// @param _kyc Is the user allowed to bid for any amount ?
    function setKyc(address _user, bool _kyc) external restrictedToOperators(){
        kycUsers[_user] = _kyc;
        emit KYCed(_user, _kyc);
    }


    /// @notice Check if a user is allowed to transact this amount
    /// @dev Anyone can check
    /// @param _user The address of the user
    /// @param _amount The amount of the bid
    function checkKyc(address _user, uint256 _amount) external view returns(bool){
        return (_amount <= priceLimit || kycUsers[_user]);
    }

}

File 15 of 40: CryptographKYCProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./CryptographKYCV1.sol";

/// @author Guillaume Gonnaud 2020
/// @title  Cryptograph KYC Proxy Smart Contract
/// @notice The proxied KYC : this is the contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract CryptographKYCProxiedV1 is VCProxy, CryptographKYCHeaderV1, CryptographKYCStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Calls the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }


    //No other logic code as it is all proxied

}




File 16 of 40: CryptographKYCV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;


/// @author Guillaume Gonnaud 2020
/// @title Cryptograph KYC header
/// @notice Contain all the events emitted by the Cryptograph KYC
contract CryptographKYCHeaderV1 {

    /// @dev Event fired whenever a wallet address is added or removed from the list of KYCED wallet
    event KYCed(address indexed _user, bool indexed _isValid);

    /// @dev Event fired whenever a new price (in wei) is set for the KYC limit
    event PriceLimit(uint256 indexed _newPrice);
}



/// @author Guillaume Gonnaud 2020
/// @title Cryptograph KYC  Storage Internal
/// @notice Contain all the storage of the Cryptograph KYC  declared in a way that don't generate getters for Proxy use
contract CryptographKYCStorageInternalV1 {

    //Perpetual Altruism, the creator of this smart contract
    address internal perpetualAltruism;

    //The list of wallets that can publish a new price limit  & add/remove wallets from the kyc
    mapping(address => bool) internal authorizedOperators;

    //The price in wei above which a transaction need a KYC. set to 0 to refuse all transactions, to UINT256MAX to allow all.
    uint256 internal priceLimit;

    //The mapping of KYCED users
    mapping(address => bool) internal kycUsers;

}

/// @author Guillaume Gonnaud 2020
/// @title Cryptograph KYC  Storage Internal
/// @notice Contain all the storage of the Cryptograph KYC  declared in a way that don't generate getters for Proxy use
contract CryptographKYCStoragePublicV1 {

    //Perpetual Altruism, the creator of this smart contract
    address public perpetualAltruism;

    //The list of wallets that can publish a new price limit  & add/remove wallets from the kyc
    mapping(address => bool) public authorizedOperators;

    //The price in wei above which a transaction need a KYC. set to 0 to refuse all transactions, to UINT256MAX to allow all.
    uint256 public priceLimit;

    //The mapping of KYCED users
    mapping(address => bool) public kycUsers;
}

File 17 of 40: EditionIndexerLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./EditionIndexerV1.sol";

/// @title Edition Indexer Logic Contract
/// @author Guillaume Gonnaud
/// @notice Provides the logic code for publishing and interacting with editions, nested into the Cryptograph Indexer
/// @dev This contract and its functions should be called by the relevant proxy smart contract only
contract EditionIndexerLogicV1 is VCProxyData, EditionIndexerHeaderV1, EditionIndexerStoragePublicV1  {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor() public
    {
        //Self intialize (nothing)
    }

    //Modifier for functions that require to be called only by the index
    modifier restrictedToIndex(){
        require((msg.sender == index), "Only the cryptograph index smart contract can call this function");
        _;
    }

    /// @notice Init function of the Indexer, starting at index 1
    /// @dev Callable only once after instanciation
    /// @param _index The address of the parent, main indexer
    /// @param _minter The address of the minter for this edition
    /// @param _editionSize The maximum number of cryptographs in this edition
    /// @return true
    function init(address _index, address _minter, uint256 _editionSize) external returns(bool){
        require(!initialized, "This Edition Indexer has already been initialized");
        index = _index;
        minter = _minter;
        editionSize = _editionSize;
        initialized = true;
        cryptographs.push(address(0x0)); //There is no cryptograph edition with serial 0
        return true;
    }

    /// @notice Init function of the Indexer, starting at index 0
    /// @dev Callable only once after instanciation
    /// @param _index The address of the parent, main indexer
    /// @param _minter The address of the minter for this edition
    /// @param _editionSize The maximum number of cryptographs in this edition
    /// @return true
    function init0(address _index, address _minter, uint256 _editionSize) external returns(bool){
        require(!initialized, "This Edition Indexer has already been initialized");
        index = _index;
        minter = _minter;
        editionSize = _editionSize;
        initialized = true;
        return true;
    }

    /// @notice Insert a cryptograph in the array and return the new index position
    /// @dev Callable only by the index
    /// @param _cryptograph The address of the inserted cryptograph
    /// @param _minter The address of the minter for this cryptograph
    /// @return The new position in the array
    function insertACryptograph(address _cryptograph, address _minter) external restrictedToIndex() returns(uint){
        require(cryptographs.length <= editionSize, "The full amount of Cryptographs for this edition has been published");
        require(_minter == minter, "Only the publisher can mint new Cryptographs for this edition");
        cryptographs.push(_cryptograph);
        return (cryptographs.length - 1); //Inserting the cryptograph and returning the position in the array
    }

    /// @notice Insert a cryptograph in the array at a specific position
    /// @dev Callable only by the index. Must be smaller than edition size. HAS A LOOP.
    /// @param _cryptograph The address of the inserted cryptograph
    /// @param _index The desired position
    function insertACryptographAt(address _cryptograph, uint256 _index) external restrictedToIndex(){

        if(cryptographs.length <= _index){
            while(cryptographs.length <= _index){
                cryptographs.push();
            }
        }
        cryptographs[_index] = _cryptograph; //Inserting the cryptograph
    }

}


File 18 of 40: EditionIndexerProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./EditionIndexerV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Cryptograph Edition Indexer Proxy Smart Contract
/// @notice The proxied Edition Indexer : this is the contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract EditionIndexerProxiedV1 is VCProxy, EditionIndexerHeaderV1, EditionIndexerStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Calls the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }

    //No other logic code as it is all proxied

}




File 19 of 40: EditionIndexerV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Edition Indexer Header
/// @notice Contain all the events emitted by the Edition Indexer
contract EditionIndexerHeaderV1 {
}


/// @author Guillaume Gonnaud 2019
/// @title Edition Indexer Storage Internal
/// @notice Contain all the storage of the Edition Indexer declared in a way that don't generate getters for Proxy use
contract EditionIndexerStorageInternalV1 {
    bool internal initialized; //Bool to check if the indexer have been initialized
    address internal minter; //The address of the minter, the only person allowed to add new cryptographs
    address internal index; //The address of the index, the only address allowed to interact with the publishing functions
    uint256 internal editionSize; //The total amount of cryptographs to be minted in this edition
    address[] internal cryptographs;
}


/// @author Guillaume Gonnaud 2019
/// @title Edition Indexer Storage Public
/// @notice Contain all the storage of the Edition Indexer declared in a way that generate getters for Logic use
contract EditionIndexerStoragePublicV1 {
    bool public initialized; //Bool to check if the index has been initialized
    address public minter; //The address of the minter, only person allowed to add new cryptographs
    address public index; //The address of the index, only address allowed to interact with the publishing functions
    uint256 public editionSize; //The total amount of cryptographs to be minted in this edition
    address[] public cryptographs;
}


File 20 of 40: ERC2665LogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./ERC2665V1.sol";
import "./TheCryptographLogicV1.sol";

/// @author Guillaume Gonnaud 2020
/// @title  Cryptograph ERC2665 Mimic Smart Contract
/// @notice Provide the logic code for third parties to read cryptographs as if they were ERC2665 tokens (they are not, hence no "write" interactions are enabled)
contract ERC2665LogicV1 is VCProxyData, ERC2665HeaderV1, ERC2665StoragePublicV1 {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor() public {
        //Self intialize (nothing)
    }

    function init(address _auctionHouse, address _indexCry) external{
        require(auctionHouse == address(0), "Already initialized");
        auctionHouse = payable(_auctionHouse);
        indexCry = _indexCry;
    }

    /// @notice Transfer a cryptograph in the ERC2665 proxy
    /// @dev Call the internal transfer function
    /// @param _from The address of the previous owner
    /// @param _to The address of the new owner
    /// @param _cryptograph The address of the cryptrograph
    /// @param _lastSoldFor The amount of the last cryptograph platform transaction for this cryptograph
    function transferACryptograph(address _from, address _to, address _cryptograph, uint256 _lastSoldFor ) external {
        require((msg.sender == auctionHouse), "Only the cryptograph auction house smart contract can call this function");
        transferACryptographInternal(_from, _to, _cryptograph, _lastSoldFor);
    }


    //Called by the Index when a minting is happening
    function MintACryptograph(address _newCryptograph) external {
        require((msg.sender == indexCry), "Only the cryptograph index smart contract can call this function");
        index2665ToAddress[totalSupplyVar] = _newCryptograph;
        totalSupplyVar++;
        balanceOfVar[address(0)] = balanceOfVar[address(0)] + 1;
        isACryptograph[_newCryptograph] = true;

        //Weakness in ERC-721 spec : Created and assigned to address 0.
        //Meaning : let's not emit event
        // emit Transfer(address(0), address(0), uint256(_newCryptograph));
    }

    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external pure returns(bool) {

        return (
            interfaceID == 0x80ac58cd || //ERC721
            interfaceID == 0x5b5e139f || //metadata extension
            interfaceID == 0x780e9d63 || //enumeration extension
            interfaceID == 0x509ffea4 //ERC2665
        );
        
    }

    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256){
        require(_owner != address(0), "ERC721 NFTs assigned to the zero address are considered invalid");
        return balanceOfVar[_owner];
    }

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address){
        require(isACryptograph[address(_tokenId)], "_tokenId is not a Valid Cryptograph");
        address retour = TheCryptographLogicV1(address(_tokenId)).owner();
        require(retour != address(0),
            "ERC721 NFTs assigned to the zero address are considered invalid");
        return retour;
    }

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `msg.value` < `getTransferFee(_tokenId)`.
    ///  If the fee is not to be paid in ETH, then token publishers SHOULD provide a way to pay the
    ///  fee when calling this function or it's overloads, and throwing if said fee is not paid.
    ///  Throws if `_to` is the zero address. Throws if `_tokenId` is not a valid NFT.
    ///  When transfer is complete, this function checks if `_to` is a smart
    ///  contract (code size > 0). If so, it calls `onERC2665Received` on `_to`
    ///  and throws if the return value is not
    ///  `bytes4(keccak256("onERC2665Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable{
        transferFromInternal(_from, _to, _tokenId, msg.sender, msg.value);

        require(_to != address(0));
        if(isContract(_to)){
            //bytes4(keccak256("onERC2665Received(address,address,uint256,bytes)")) == bytes4(0xac3cf292)
            require(ERC2665TokenReceiver(_to).onERC2665Received(msg.sender, _from, _tokenId, data) == bytes4(0xac3cf292));
        }
    }

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable{
        transferFromInternal(_from, _to, _tokenId, msg.sender, msg.value);

        require(_to != address(0));
        if(isContract(_to)){
            //bytes4(keccak256("onERC2665Received(address,address,uint256,bytes)")) == bytes4(0xac3cf292)
            require(ERC2665TokenReceiver(_to).onERC2665Received(msg.sender, _from, _tokenId, "") ==  bytes4(0xac3cf292));
        }
    }

   /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. Throws if `msg.value` < `getTransferFee(_tokenId)`.
    ///  If the fee is not to be paid in ETH, then token publishers SHOULD provide a way to pay the
    ///  fee when calling this function and throw if said fee is not paid.
    ///  Throws if `_to` is the zero address. Throws if `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable{
        transferFromInternal(_from, _to, _tokenId, msg.sender, msg.value);
    }



    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner. After a successful call and if
    ///  `msg.value == getTransferFee(_tokenId)`, then a subsequent atomic call to
    ///  `getTransferFee(_tokenId)` would eval to 0. If the fee is not to be paid in ETH,
    ///  then token publishers MUST provide a way to pay the fee when calling this function,
    ///  and throw if the fee is not paid.
    ///  Any ETH sent to this function will be used to pay the transfer fee, and if the
    ///  ETH sent is twice (or more) the non-0 current transfer fee, the next transfer fee 
    ///  will be prepaid as well.  
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable{

        address owner = TheCryptographLogicV1(address(_tokenId)).owner();
        require(msg.sender == owner || approvedOperator[owner][msg.sender], "Only the owner or an operator can approve a token transfer");
        require(isACryptograph[address(_tokenId)], "_tokenId is not a Valid Cryptograph");

        //Reset the renatus timer
        TheCryptographLogicV1(address(_tokenId)).renatus();

        uint256 leftover = msg.value;

        //If the transfer fee is being paid
        if(leftover >= transferFees[_tokenId]){

            leftover =  leftover - transferFees[_tokenId];
            transferFees[_tokenId] = 0;
            
            //Prepay the next subsequent transfer
            if(leftover >= (lastSoldFor[_tokenId] * 15 /100)){
                leftover = leftover -  (lastSoldFor[_tokenId] * 15 /100);
                transferFeePrepaid[_tokenId] = true;
            }

        }

        //Marking the auction has now being handled by ERC2665
        AuctionHouseLogicV1(auctionHouse).approveERC2665{value: msg.value - leftover }(address(_tokenId), msg.sender, _approved);

        if(leftover != 0){
            //Send back the extra money to the payer
            (bool trashBool, ) = msg.sender.call{value:leftover}("");
            require(trashBool, "Could not send the leftover money back");
        }

        approvedTransferAddress[_tokenId] = _approved; 

        emit Approval(msg.sender, _approved, _tokenId);

    }

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external {
        approvedOperator[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address){
        require(isACryptograph[address(_tokenId)], "_tokenId is not a Valid Cryptograph");

        return approvedTransferAddress[_tokenId];
    }
  
    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool){
        return approvedOperator[_owner][_operator];
    }

    /// @notice Query what is the transfer fee for a specific token
    /// @dev If a call would returns 0, then any subsequent calls witht the same argument
    /// must also return 0 until the Transfer event has been emitted.
    /// @param _tokenId The NFT to find the Transfer Fee amount for
    /// @return The amount of Wei that need to be sent along a call to a transfer function
    function getTransferFee(uint256 _tokenId) external view returns (uint256){
        return transferFees[_tokenId];
    }


    /// @notice Query what is the transfer fee for a specific token if the fee is to be paid
    /// @dev If a call would returns 0, then any subsequent calls with the same arguments
    /// must also return 0 until the Transfer event has been emitted. If _currencySymbol == 'ETH',
    /// then this function must return the same result as if `getTransferFee(uint256 _tokenId)` was called.
    /// @param _tokenId The NFT to find the Transfer Fee amount for
    /// @param _currencySymbol The currency in which the fee is to be paid
    /// @return The amount of Wei that need to be sent along a call to a transfer function
    function getTransferFee(uint256 _tokenId, string calldata _currencySymbol) external view returns (uint256){
        //keccak256(bytes("ETH")) == bytes32(0xaaaebeba3810b1e6b70781f14b2d72c1cb89c0b2b320c43bb67ff79f562f5ff4)
        if(bytes32(0xaaaebeba3810b1e6b70781f14b2d72c1cb89c0b2b320c43bb67ff79f562f5ff4) == keccak256(bytes(_currencySymbol))){
            return transferFees[_tokenId];
        } else {
            return 0;
        }
    }


    function name() external pure returns(string memory _name){
        return "Cryptograph";
    }

    function symbol() external pure returns(string memory _symbol){
        return "Cryptograph";
    }

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns(string memory){
        require(isACryptograph[address(_tokenId)], "_tokenId is not a Valid Cryptograph");
   
        return string(abi.encodePacked("https://cryptograph.co/tokenuri/", addressToString(address(_tokenId))));
    }


    /// @notice Count NFTs tracked by this contract
    /// @return A count of valid NFTs tracked by this contract, where each one of
    ///  them has an assigned and queryable owner not equal to the zero address
    function totalSupply() external view returns (uint256){
        return totalSupplyVar;
    }

    /// @notice Enumerate valid NFTs
    /// @dev Throws if `_index` >= `totalSupply()`.
    /// @param _index A counter less than `totalSupply()`
    /// @return The token identifier for the `_index`th NFT,
    ///  (sort order not specified)
    function tokenByIndex(uint256 _index) external view returns (uint256){
        require(_index < totalSupplyVar, "index >= totalSupply()");
        return uint256(index2665ToAddress[_index]);
    }

    /// @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `_index` >= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid NFTs.
    /// @param _owner An address where we are interested in NFTs owned by them
    /// @param _index A counter less than `balanceOf(_owner)`
    /// @return The token identifier for the `_index`th NFT assigned to `_owner`,
    ///   (sort order not specified)
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256){
        require(_owner != address(0), "_owner == 0");
        require(_index < balanceOfVar[_owner], "_index >= balanceOf(_owner)");

        return indexedOwnership[_owner][_index];
    }

    /// @notice Get the address of a Cryptograph from their tokenID
    /// @dev literally just a typecast
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the Cryptograph that would be associated with this ID
    function addressFromTokenId(uint256 _tokenId) external pure returns (address){
            return address(_tokenId);
    }

    /// @notice Get the tokenID of a Cryptograph from their address
    /// @dev literally just a typecast
    /// @param _tokenAddress The address for a Cryptograph
    /// @return The tokenId of the Cryptograph that would be associated with this address
    function tokenIdFromAddress(address _tokenAddress) external pure returns (uint256){
            return uint256(_tokenAddress);
    }

    /// @notice Extend the renatus timer for a cryptograph
    /// @dev only callable by approved operators or the owner
    /// @param _tokenId The identifier for an NFT
    function renatus(uint256 _tokenId) public {
        require(isACryptograph[address(_tokenId)], "renatus need to be called for a Valid Cryptograph");

        address owner = TheCryptographLogicV1(address(_tokenId)).owner();
        require(approvedOperator[owner][msg.sender] || owner == msg.sender);

        //Extending the renatus timer
        TheCryptographLogicV1(address(_tokenId)).renatus();
    }

    /// @notice Emit a Renatus transfer event destroying the token for it to be reborn later
    /// @dev only callable by the token itself
    function triggerRenatus() public{
        require(isACryptograph[msg.sender], "Only the token itself can notify us of a renatus hapenning");
        emit Transfer(TheCryptographLogicV1(address(msg.sender)).owner(), address(0), uint256(msg.sender));
    }
    
    /// @notice Transfer a cryptograph in the ERC2665 proxy
    /// @dev Will fire the Transfer event and update the enumerable arrays, as well as setting the new transfer fee
    /// @param _from The address of the previous owner
    /// @param _to The address of the new owner
    /// @param _cryptograph The address of the cryptrograph
    /// @param _lastSoldFor The amount of the last cryptograph platform transaction for this cryptograph
    function transferACryptographInternal(address _from, address _to, address _cryptograph, uint256 _lastSoldFor) internal{

        //Check that the Cryptograph exist
         require(isACryptograph[_cryptograph], 
            "Only minted cryptogrtaphs can be transferred");

        //Adjusting the last sold and transfer fees
        if(_lastSoldFor != lastSoldFor[uint256(_cryptograph)]){
            lastSoldFor[uint256(_cryptograph)] = _lastSoldFor;
        }

        //Checking if the fee was prepaid
        if(!transferFeePrepaid[uint256(_cryptograph)]){
            transferFees[uint256(_cryptograph)] = (_lastSoldFor * 15) / 100; //15% transfer fee
        } else {
            transferFees[uint256(_cryptograph)] = 0;
        }
        transferFeePrepaid[uint256(_cryptograph)] = false;
  

        //Reseting the approved address
        approvedTransferAddress[uint256(_cryptograph)] = address(0);


        //Emitting the event
        emit Transfer(_from, _to, uint256(_cryptograph));

        uint256 posInArray;

        //Adjusting the ownership array of the actors
        if(_from != address(0x0)){

            if(balanceOfVar[_from] != 1){

                //Case where we still have more cryptographs in the index.

                //Grabbing the position of the transferred cryptograph in the previous owner ownership array
                posInArray = cryptographPositionInOwnershipArray[uint256(_cryptograph)];

                //Replacing this position content with the content of the last element in the owner array;
                indexedOwnership[_from][posInArray] = indexedOwnership[_from][balanceOfVar[_from]-1];

                //Updating the last element new index position
                cryptographPositionInOwnershipArray[indexedOwnership[_from][posInArray]] = posInArray;

                //refund some gas
                delete indexedOwnership[_from][balanceOfVar[_from]-1];

            }  else {
                //refund some gas
                delete indexedOwnership[_from][0];
            }
        }

        //Some caching
        posInArray = balanceOfVar[_to];

        //Adjusting the arrays of the receiver
        if(_to != address(0x0)){

            if(indexedOwnership[_to].length < posInArray + 1){
                indexedOwnership[_to].push(uint256(_cryptograph));
            } else {
                indexedOwnership[_to][posInArray] = uint256(_cryptograph);
            }

            cryptographPositionInOwnershipArray[uint256(_cryptograph)] = posInArray;
        }

        //Adjusting the balance of the actors
        balanceOfVar[_from] = balanceOfVar[_from] - 1;
        balanceOfVar[_to] = balanceOfVar[_to] + 1;

    }


    /// @notice transferACryptograph following a TransferFrom call to an ERC2665 endpoint
    /// @dev Will call the transferInternal as part of the process as well as notify the ecosystem of necessary changes.
    /// @param _from The address of the previous owner
    /// @param _to The address of the new owner
    /// @param _tokenId The tokenID of the cryptrograph
    /// @param _sender The adress of the msg.sender of the endpoint
    /// @param _value The amount of ETH paid with the endpoint call
    function transferFromInternal(address _from, address _to, uint256 _tokenId, address _sender, uint256 _value) internal{

        //Check that the fee is being paid
        require(_value >= transferFees[_tokenId], 
            "The transfer fee must be paid");

        //Check that the _from token owner is correct
        address owner = TheCryptographLogicV1(address(_tokenId)).owner();
        require(owner == _from,
            "The owner of the token and _from did not match");

        //Check that the msg.sender is legitimate to manipulate the token
        require(_sender == owner || approvedOperator[owner][_sender] || approvedTransferAddress[_tokenId] == _sender, "The caller is not allowed to transfer the token");

        //Calculate how much extra fee was sent
        uint256 leftover = _value - transferFees[_tokenId];

        //ERC2665 Transfer
        transferACryptographInternal(_from, _to, address(_tokenId), lastSoldFor[_tokenId]);

        //Actual Transfer will also check that there is no auction going on
        AuctionHouseLogicV1(auctionHouse).transferERC2665{value:  _value - leftover}(address(_tokenId), _sender, _to);

        //Check if the next fee is also paid
        if(leftover >= transferFees[_tokenId]){
            //pay the next transfer fee
            leftover =  leftover - transferFees[_tokenId];
            transferFees[_tokenId] = 0;
        }

        if(leftover != 0){
            //Send back the extra money to the payer
            (bool trashBool, ) = _sender.call{value:leftover}("");
            require(trashBool, "Could not send the leftover money back");
        }
    }


    /// @notice Convert an Ethereum address to a human readable string
    /// @param _addr The adress you want to convert
    /// @return The address in 0x... format
    function addressToString(address _addr) internal pure returns(string memory)
    {
        bytes32 addr32 = bytes32(uint256(_addr)); //Put the address 20 byte address in a bytes32 word
        bytes memory alphabet = "0123456789abcdef";  //What are our allowed characters ?

        //Initializing the array that is gonna get returned
        bytes memory str = new bytes(42);

        //Prefixing
        str[0] = '0';
        str[1] = 'x';

        for (uint256 i = 0; i < 20; i++) { //iterating over the actual address

            /*
                proper offset : output starting at 2 because of '0X' prefix, 1 hexa char == 2 bytes.
                input starting at 12 because of 12 bytes of padding, byteshifted because 2byte == 1char
            */
            str[2+i*2] = alphabet[uint8(addr32[i + 12] >> 4)];
            str[3+i*2] = alphabet[uint8(addr32[i + 12] & 0x0f)];
        }
        return string(str);
    }

    /// @notice Check if an address is a contract
    /// @param _address The adress you want to test
    /// @return true if the address has bytecode, false if not
    function isContract(address _address) internal view returns(bool){
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        assembly { codehash := extcodehash(_address) }
        return (codehash != accountHash && codehash != 0x0);
    }
}

File 22 of 40: ERC2665V1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity ^0.6.6;

/// @title ERC-2665 NFT Transfer Fee Extension
/// @dev See https://github.com/ethereum/EIPs/issues/2665
///  Note: the ERC-165 identifier for this interface is 0x509ffea4.
///  Note: you must also implement the ERC-165 identifier of ERC-721, which is 0x80ac58cd.
interface ERC2665 /* is ERC165, is ERC721 but overide it's Design by contract specifications */ {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). Exception: during contract creation, any number of NFTs
    ///  may be created and assigned without emitting Transfer. At the time of
    ///  any transfer, the approved address for that NFT (if any) is reset to none.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    /// @dev This emits when the approved address for an NFT is changed or
    ///  reaffirmed. The zero address indicates there is no approved address.
    ///  When a Transfer event emits, this also indicates that the approved
    ///  address for that NFT (if any) is reset to none.
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    /// @dev This emits when an operator is enabled or disabled for an owner.
    ///  The operator can manage all NFTs of the owner.
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `msg.value` < `getTransferFee(_tokenId)`.
    ///  If the fee is not to be paid in ETH, then token publishers SHOULD provide a way to pay the
    ///  fee when calling this function or it's overloads, and throwing if said fee is not paid.
    ///  Throws if `_to` is the zero address. Throws if `_tokenId` is not a valid NFT.
    ///  When transfer is complete, this function checks if `_to` is a smart
    ///  contract (code size > 0). If so, it calls `onERC2665Received` on `_to`
    ///  and throws if the return value is not
    ///  `bytes4(keccak256("onERC2665Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. Throws if `msg.value` < `getTransferFee(_tokenId)`.
    ///  If the fee is not to be paid in ETH, then token publishers SHOULD provide a way to pay the
    ///  fee when calling this function and throw if said fee is not paid.
    ///  Throws if `_to` is the zero address. Throws if `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner. After a successful call and if
    ///  `msg.value == getTransferFee(_tokenId)`, then a subsequent atomic call to
    ///  `getTransferFee(_tokenId)` would eval to 0. If the fee is not to be paid in ETH,
    ///  then token publishers MUST provide a way to pay the fee when calling this function,
    ///  and throw if the fee is not paid.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable;

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external;

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address);

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);

    /// @notice Query what is the transfer fee for a specific token
    /// @dev If a call would returns 0, then any subsequent calls witht the same argument
    /// must also return 0 until the Transfer event has been emitted.
    /// @param _tokenId The NFT to find the Transfer Fee amount for
    /// @return The amount of Wei that need to be sent along a call to a transfer function
    function getTransferFee(uint256 _tokenId) external view returns (uint256);

    /// @notice Query what is the transfer fee for a specific token if the fee is to be paid
    /// @dev If a call would returns 0, then any subsequent calls with the same arguments
    /// must also return 0 until the Transfer event has been emitted. If _currencySymbol == 'ETH',
    /// then this function must return the same result as if `getTransferFee(uint256 _tokenId)` was called.
    /// @param _tokenId The NFT to find the Transfer Fee amount for
    /// @param _currencySymbol The currency in which the fee is to be paid
    /// @return The amount of Wei that need to be sent along a call to a transfer function
    function getTransferFee(uint256 _tokenId, string calldata _currencySymbol) external view returns (uint256);

}


interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

/// @dev Note: the ERC-165 identifier for this interface is 0xac3cf292.
interface ERC2665TokenReceiver {
    /// @notice Handle the receipt of an NFT
    /// @dev The ERC2665 smart contract calls this function on the recipient
    ///  after a `transfer`. This function MAY throw to revert and reject the
    ///  transfer. Return of other than the magic value MUST result in the
    ///  transaction being reverted.
    ///  Note: the contract address is always the message sender.
    /// @param _operator The address which called `safeTransferFrom` function
    /// @param _from The address which previously owned the token
    /// @param _tokenId The NFT identifier which is being transferred
    /// @param _data Additional data with no specified format
    /// @return `bytes4(keccak256("onERC2665Received(address,address,uint256,bytes)"))`
    ///  unless throwing
    function onERC2665Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
}

/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface ERC721Metadata /* is ERC721 */ {
    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external view returns(string memory _name);

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external view returns(string memory _symbol);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns(string memory);
}

/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x780e9d63.
interface ERC721Enumerable /* is ERC721 */ {
    /// @notice Count NFTs tracked by this contract
    /// @return A count of valid NFTs tracked by this contract, where each one of
    ///  them has an assigned and queryable owner not equal to the zero address
    function totalSupply() external view returns (uint256);

    /// @notice Enumerate valid NFTs
    /// @dev Throws if `_index` >= `totalSupply()`.
    /// @param _index A counter less than `totalSupply()`
    /// @return The token identifier for the `_index`th NFT,
    ///  (sort order not specified)
    function tokenByIndex(uint256 _index) external view returns (uint256);

    /// @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `_index` >= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid NFTs.
    /// @param _owner An address where we are interested in NFTs owned by them
    /// @param _index A counter less than `balanceOf(_owner)`
    /// @return The token identifier for the `_index`th NFT assigned to `_owner`,
    ///   (sort order not specified)
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}

contract ERC2665HeaderV1 {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). Exception: during contract creation, any number of NFTs
    ///  may be created and assigned without emitting Transfer. At the time of
    ///  any transfer, the approved address for that NFT (if any) is reset to none.
    event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 indexed _tokenId);

    /// @dev This emits when the approved address for an NFT is changed or
    ///  reaffirmed. The zero address indicates there is no approved address.
    ///  When a Transfer event emits, this also indicates that the approved
    ///  address for that NFT (if any) is reset to none.
    event Approval(
        address indexed _owner,
        address indexed _approved,
        uint256 indexed _tokenId);

    /// @dev This emits when an operator is enabled or disabled for an owner.
    ///  The operator can manage all NFTs of the owner.
    event ApprovalForAll(
        address indexed _owner,
        address indexed _operator,
        bool _approved);
}

contract ERC2665StorageInternalV1 {
    address payable internal auctionHouse;
    address internal indexCry;

    mapping(address => bool) internal isACryptograph;

    mapping(address => uint256) internal balanceOfVar;

    uint256 internal totalSupplyVar;
    mapping(uint256 => address) internal index2665ToAddress;

    mapping(address => uint256[]) internal indexedOwnership; //[Owner][index] = cryptographID
    mapping(uint256 => uint256) internal cryptographPositionInOwnershipArray; // [cryptographID] = index
    mapping(uint256 => uint256) internal lastSoldFor; //Value last sold on the cryptograph platform
    mapping(uint256 => uint256) internal transferFees; //Pending transfer fee
    mapping(uint256 => bool) internal transferFeePrepaid; //Have the next transfer fee be prepaid ?
    mapping(uint256 => address) public approvedTransferAddress; //Address allowed to Transfer a token
    mapping(address => mapping(address => bool)) internal approvedOperator; //Approved operators mapping

}

contract ERC2665StoragePublicV1 {
    address payable public auctionHouse;
    address public indexCry;

    mapping(address => bool) public isACryptograph;

    mapping(address => uint256) public balanceOfVar;

    uint256 public totalSupplyVar;
    mapping(uint256 => address) public index2665ToAddress;

    mapping(address => uint256[]) public indexedOwnership; //[Owner][index] = cryptographID
    mapping(uint256 => uint256) public cryptographPositionInOwnershipArray; // [cryptographID] = index
    mapping(uint256 => uint256) public lastSoldFor; // Value last sold on the cryptograph platform
    mapping(uint256 => uint256) public transferFees; // Pending transfer fee
    mapping(uint256 => bool) public transferFeePrepaid; //Have the next transfer fee be prepaid ?
    mapping(uint256 => address) public approvedTransferAddress; //Address allowed to Transfer a token
    mapping(address => mapping(address => bool)) public approvedOperator; //Approved operators mapping
}

File 23 of 40: Migrations.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity >=0.4.25 <0.7.0;

//Only used by truffle due to their design choices. Safe to ignore in any other context

contract Migrations {
    address public owner;
    uint256 public last_completed_migration;

    modifier restricted() {
        if (msg.sender == owner) _;
    }

    constructor() public {
        owner = msg.sender;
    }

    function setCompleted(uint256 completed) public restricted {
        last_completed_migration = completed;
    }

    function upgrade(address new_address) public restricted {
        Migrations upgraded = Migrations(new_address);
        upgraded.setCompleted(last_completed_migration);
    }
}

File 24 of 40: MintingAuctionLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./MintingAuctionV1.sol";
import "./CryptographFactoryV1.sol";
import "./AuctionHouseLogicV1.sol";
import "./TheCryptographLogicV1.sol";
import "./CryptographInitiator.sol";
import "./BidLink.sol";

/*
    This contract idea is rather complex.
    => Implement a Generalized GBM Auction (GGBMA) for initial supply of a series of limited editions within the same auction (instead of each token being sold independantly)

    >During the initial sale, everyone can place a bid at any amount of money (one exception, see below)
    >You can't retract your bid
    >You can only have one active bid, and can only re-bid higher than YOUR previous bid
    >If you want to place a bid higher than the current highest bid, it need to be at least 5% higher
    >When the top bid is being displaced by a new bid, the previous top bid owner receive GBM incentives. All others bidders don't
    >No new bid can be placed after the initial period
    >At the end of the auction, you can mint a cryptograph where the serial # match your position in the auction
        -> Highest bidder get #1, second highest get #2, etc...
    >If there was a limited supply, any bidder that was ranked below the supply amount can cancel their bid and recover the eth

*/

/// @author Guillaume Gonnaud 2019
/// @title Minting Auction Logic Code
/// @notice Based on the Single Auction smart contracts but with overrides
contract MintingAuctionLogicV1 is VCProxyData, MintingAuctionHeaderV1, MintingAuctionStoragePublicV1 {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall hence its memory state is irrelevant
    constructor() public
    {
        //Self intialize (nothing)
    }

    //Modifier for functions that requires to be called only by the Auction house
    modifier restrictedToAuctionHouse(){
        require((msg.sender == auctionHouse), "Only the auction house smart contract can call this function");
        _;
    }

    /// @notice Init function of the MintingAuction
    /// @param _myCryptograph The address of the cryptograph this auction is paired with
    /// @param _cryInitiator The address of the initator containing the details of our auction
    /// @param _initialize true => can't change any spec afterward. false => can initialize again.
    function initAuction(
            address _myCryptograph,
            address _cryInitiator,
            bool _initialize
        ) public {

        require(!initialized, "This auction is already initialized");
        initialized = _initialize; //Are we locking ?

        //we must be either perpetual altruism OR never inited before
        require(auctionHouse == address(0) || msg.sender == cryFactory,"Only Perpetual altruism can change a yet to be locked auction");
        cryFactory = msg.sender;

        /*
        ==================================================
                            Bidding section
        ==================================================
        */

        startingPrice = CryptographInitiator(_cryInitiator).startingPrice(); //The first bid that needs to be outbid is 1 Wei
        sellingPrice = 0; //A newly minted Cryptograph does not have an owner willing to sell

        /*
        ==================================================
                        Calculations section
        ==================================================
        */
        bid_Decimals = 100000;  //100k, or 100%
        bid_incMax = 10000; //10k, or 10%
        bid_incMin = 1000; //1k, or 1%
        bid_stepMin = 10500; //10.5k, or 10.5%
        bid_cutOthers = 500; // 500, or 0.5%
        bid_multiplier = 9000; //9000 = Doubling bid yield max gain (1%+9% = 10%)

        /*
        ==================================================
                            Money section
        ==================================================
        */
        //Setting up money flow
        perpertualAltruism = CryptographFactoryStoragePublicV1(cryFactory).officialPublisher();
        perpetualAltruismCut = CryptographInitiator(_cryInitiator).perpetualAltruismCut();
        publisher = CryptographInitiator(_cryInitiator).publisher();
        publisherCut = CryptographInitiator(_cryInitiator).publisherCut();
        charity = CryptographInitiator(_cryInitiator).charity();
        charityCut = CryptographInitiator(_cryInitiator).charityCut();
        thirdParty = CryptographInitiator(_cryInitiator).thirdParty();
        thirdPartyCut = CryptographInitiator(_cryInitiator).thirdPartyCut();
        maxSupply = CryptographInitiator(_cryInitiator).maxSupply();

        //Setting up timings

        startTime = CryptographInitiator(_cryInitiator).auctionStartTime();
        endTime = CryptographInitiator(_cryInitiator).auctionStartTime() + CryptographInitiator(_cryInitiator).auctionSecondsDuration();

        auctionHouse = CryptographFactoryStoragePublicV1(cryFactory).targetAuctionHouse();
        myCryptograph = _myCryptograph;
        initiator = _cryInitiator;
    }

    /// @notice Make an official auction unmodifiable once we are certain the parameters are correct
    /// @dev Only callable by perpetual altruism
    function lock() external{
        require(msg.sender == cryFactory, "Only Perpetual altruism can lock the initialization");
        initialized = true;
    }

    /// @notice Place a bid to own a cryptograph and distribute the incentives
    /// @dev Only callable by the Auction House
    /// @param _newBidAmount The amount of the new bid
    /// @param _newBidder The address of the bidder
    function bid(uint256 _newBidAmount, address _newBidder) external payable restrictedToAuctionHouse(){

        /*
        ========================== money check ==========================
        */

        //Unitiliazed cryptographs can't be bid upon
        require(initialized, "This auction has not been properly set up yet");
        //Did we send the proper amount of money, are we allowed to bid ?
        require(_newBidAmount == msg.value + currentBids[_newBidder], "Amount of money sent incorrect"); //Also protects from self-underbiding
        //check to be made : is the new bid big enough ?
        require(numberOfBids != maxSupply || currentBids[tailBidder] < _newBidAmount, "Your bid is lower than the lowest bid");

        require( //Either fresh bid OR meeting the standing bid * the step OR below highest bidder
                    ( (highestBidder == address(0)) && startingPrice <= _newBidAmount ) ||
                    ( (highestBidder != address(0)) && (currentBids[highestBidder] * (bid_Decimals + bid_stepMin) <= (_newBidAmount * bid_Decimals) )) ||
                    (  (highestBidder != address(0)) && (currentBids[highestBidder] >= _newBidAmount) ),
                "New bid amount does not meet an authorized amount");

        /*
        ========================== Timing check ==========================
        */

        //We must be past the initial auction start
        require(now >= startTime, "You can only bid once the initial auction has started");
        require(now < endTime, "GGMBA do not allow bidding past the ending time");

        //Emit the bid acceptance event before triggering the payouts
        emit BidAccepted(_newBidAmount, _newBidder);

        /*
        ========================== Payouts ==========================
        */

        uint256 duePay;
        //if we are not an underbidder...
        if((currentBids[highestBidder] < _newBidAmount)){
            //In a GGBMA, every new highest bidder pays a 0.5% fee
            duePay = (_newBidAmount * bid_cutOthers)/bid_Decimals;
            unsettledPayouts += duePay;
            distributeStakeholdersPayouts(duePay, _newBidder);
            //Send the payout to the previous highest bidder
            if(highestBidder != address(0)){
                duePay = duePayout[highestBidder];
                if(duePay != 0){
                    unsettledPayouts += duePay;
                    emit Payout(duePay,  highestBidder,  _newBidder);
                    AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: duePay }(highestBidder, _newBidder);
                }
            }

            /*
            ========================== Reward ==========================
            */

            //Set the new payout amount we will receive when outbid (Only for new highest bidders)
            calculateReward(_newBidAmount, _newBidder);
        }

        /*
        ===================== Bid Cancellation And Registering =====================
        */
        uint256 toSend;

        //We need to cancel our own previous lower bid OR to update the number of bids
        if(currentBids[_newBidder] != 0){

            BidLink(bidLinks[_newBidder]).setBidAmount(_newBidAmount); //Updating our bid amount

            //Updating our neigbors link
            if( BidLink(bidLinks[_newBidder]).above() != address(0x0)){
                BidLink(BidLink(bidLinks[_newBidder]).above()).setBelow(BidLink(bidLinks[_newBidder]).below()); //Unlinking above us
            }

            if( BidLink(bidLinks[_newBidder]).below() != address(0x0)){
                BidLink(BidLink(bidLinks[_newBidder]).below()).setAbove(BidLink(bidLinks[_newBidder]).above()); //Unlinking below us
            }
            emit BidCancelled(currentBids[_newBidder], currentBids[_newBidder], _newBidder); //Emitting the event

        } else {
            //Create a bid link
            bidLinks[_newBidder] = address(new BidLink(_newBidder, _newBidAmount));

            //Refunding/Setting the tail.
            if(numberOfBids == maxSupply && maxSupply != 0){
                //Max number of bids reached, refunding the tail bid
                toSend = currentBids[tailBidder];
                currentBids[tailBidder] = 0;
                if(toSend != 0){
                    //Send back all the money : no payout settlement required
                    emit BidCancelled(toSend, toSend, tailBidder);
                    AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(tailBidder, tailBidder);
                }
                //Updating the tail bid
                BidLink(BidLink(bidLinks[tailBidder]).above()).setBelow(address(0x0)); //Unlinking
                tailBidder = BidLink(BidLink(bidLinks[tailBidder]).above()).bidder(); //Updating the tail


            } else {
                //Max not reached
                numberOfBids++;
            }
        }



        currentBids[_newBidder] = _newBidAmount; //Set the amount of the bid

        //Browse the BidLink chain until the link above us have a bid greater or equal to us
        address currentLink = bidLinks[highestBidder];

        if(currentLink == address(0x0)){
            tailBidder = _newBidder; //We are the only bidder = we are also the lowest bidder
        } else {
            //Browsing down the linked list
            while( BidLink(currentLink).below() != address(0x0) && BidLink(BidLink(currentLink).below()).bidAmount() >= _newBidAmount){
                    currentLink = BidLink(currentLink).below(); //Browse the chain
            }
        }

        //Are we the new highest bidder ?
        if(currentBids[highestBidder] < _newBidAmount){
            highestBidder = _newBidder; //We are the highest bidder
            BidLink(bidLinks[_newBidder]).setBelow(currentLink); //Setting ourselves as above the old head
            if(currentLink != address(0x0)){    //Only if there is a previous head
                BidLink(currentLink).setAbove(bidLinks[_newBidder]); //Setting the old head as below us
            }
        } else { //Normally inserting ourself in the chain
            BidLink(bidLinks[_newBidder]).setAbove(currentLink); //Above us is the current link
            BidLink(bidLinks[_newBidder]).setBelow(BidLink(currentLink).below()); //Below us is the previous tail of the current link
            if(BidLink(bidLinks[_newBidder]).below() != address(0x0)){  //If we have a new tail
                BidLink(BidLink(bidLinks[_newBidder]).below()).setAbove(bidLinks[_newBidder]); //We are above our new tail
            } else {
                //We are the new tail
                tailBidder = _newBidder;
            }
            //We should always have a new head (as we are not highest bidder)
            BidLink(BidLink(bidLinks[_newBidder]).above()).setBelow(bidLinks[_newBidder]); //Our new head has us as a tail
        }

        //The chainlink is now ordered properly

    }

    /// @notice USed to check which can of auction we are
    /// @dev Only callable by the Auction House.
    /// @param _newOwner The address of the bidder wishing to mint a new cryptograph
    /// @return 0 if a normal auction, 1 if a minting auction
    function win(address _newOwner) external restrictedToAuctionHouse() view returns(uint){
        require(currentBids[_newOwner] != 0, "You don't have any active bid on this auction");
        require(now > endTime, "The initial auction is not over yet");

        return 1;
    }

    /// @notice Distribute the bid of an auction winner
    /// @dev Only callable by the Auction House.
    /// @param _newOwner The address of the bidder wishing to mint a new cryptograph
    function distributeBid(address _newOwner) external restrictedToAuctionHouse(){

         if(_newOwner == highestBidder){
            distributeStakeholdersPayouts(currentBids[highestBidder] - unsettledPayouts, _newOwner); //Payouts are deduced from the highest bid
        } else {
            distributeStakeholdersPayouts(currentBids[_newOwner], _newOwner);
        }

        currentBids[_newOwner] = 0;
    }

    /// @notice Function used to distribute an arbitrary amount of money among non-bidders
    /// @dev Only callable internally
    /// @param _amount The amount of money to spread
    /// @param _contributor The address of the gracious donor
    function distributeStakeholdersPayouts(uint256 _amount, address _contributor) internal{
        uint256 toDistribute = _amount;
        uint256 toSend;

        //Pay the charity
        toSend = (charityCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  charity,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(charity, _contributor);
        }

        //Pay the publisher
        toSend = (publisherCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  publisher,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(publisher, _contributor);
        }

        //Pay the thirdParty
        toSend = (thirdPartyCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  thirdParty,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(thirdParty, _contributor);
        }

        //Pay perpetual Altruism the reminder (25%). only non-null guaranteed address, so send any rounding errors there
        toSend = toDistribute;
        if(toSend != 0){
            emit Payout(toSend,  perpertualAltruism,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(perpertualAltruism, _contributor);
        }
    }

    /// @notice Calculating and setting how much payout a bidder will receive if outbid
    /// @dev Only callable internally
    /// @param _newBid The amount of money in the new bid
    /// @param _bidder The address of the new bidder
    function calculateReward(uint256 _newBid, address _bidder) internal{

        //Calculating how much payout we will receive if we are outbid

        //Init the baseline bid we need to perform against
        uint256 baseBid = currentBids[highestBidder] * (bid_Decimals + bid_stepMin) / bid_Decimals;
        if(baseBid == 0){
            baseBid = startingPrice;

            //Do not divide by 0
            if(baseBid == 0){
                baseBid = 1;
            }
        }

        //We calculate our baseline reward. We square the decimals to guarantee a granularity of at least 1/bid_Decimals instead of 1/bid_multiplier
        //This also somewhat limits the hardcap for a reward to max_UInt256/10^11 => Not a problem as this amount of eth will not be minted
        uint256 decimaledRatio = ((bid_Decimals * bid_multiplier * (_newBid - baseBid) ) / baseBid) + bid_incMin * bid_Decimals;

        //If we go over the maximum payout, we set the reward to the maximum payout
        if(decimaledRatio > (bid_Decimals * bid_incMax)){
            decimaledRatio = bid_Decimals * bid_incMax;
        }

        duePayout[_bidder] = (_newBid * decimaledRatio)/(bid_Decimals*bid_Decimals);
    }
}

File 25 of 40: MintingAuctionProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./MintingAuctionV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Minting Auction Proxy Smart Contract
/// @notice The Minting Auction proxy : this is this contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract MintingAuctionProxiedV1 is VCProxy, MintingAuctionHeaderV1, MintingAuctionStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }

}


File 26 of 40: MintingAuctionV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Minting Auction Header
/// @notice Contain all the events emitted by the Minting Auction
contract MintingAuctionHeaderV1 {
    event BidAccepted(uint256 bidValue, address bidder);
    event Payout(uint256 amount, address beneficiary, address contributor);
    event BidCancelled(uint256 bidValue, uint256 ethReturned, address bidder);
    event SaleStarted(address seller, uint256 hammerTime, uint256 hammerBlock);
    event SellingPriceAdjusted(address seller, uint256 amount);
    event Win(address buyer, address seller, uint256 bidValue);
}


/// @author Guillaume Gonnaud 2019
/// @title Minting Auction Storage Internal
/// @notice Contain all the storage of the Minting Auction declared in a way that don't generate getters for Proxy use
contract MintingAuctionStorageInternalV1 {

     /*
    ==================================================
                        Bidding section
    ==================================================
    */

    //The current bids made by each address
    mapping (address => uint) internal currentBids;

    //The current amount of wei each address receive when outbid as the highest bid.
    mapping (address => uint) internal duePayout; //How much the bidder make

    //The current highest bidder;
    address internal highestBidder;

    //The current amount of unsettled payouts distributed for the current bidding process
    uint256 internal unsettledPayouts;

    //The default starting price
    uint256 internal startingPrice;

    //The current selling price
    uint256 internal sellingPrice;

    /*
    ==================================================
                        Calculations section
    ==================================================
    */

    /*
    For a standing bid s and a new bid n, we express the return on the new bid as:

    incentive(n,s) % = min[ incmax , incmin + m * (n- s * (1+ stepmin)) / (s * (1+ stepmin))]

    Where:
    stepmin is the minimum bid increment, expressed as a fraction of the current standing bid (ex : 0.01 for 1/10 or 10%)
    incmin is the minimum incentive, expressed as a fraction
    incmax is the maximum incentive, expressed as a fraction
    m is the multiplier effect, expressed as a positive real number

    */

    //Values used to calculate the payouts.
    uint256 internal bid_Decimals; //100k, or 100%
    uint256 internal bid_incMax; //4.5k, or 4.5%
    uint256 internal bid_incMin; //1k, or 1%
    uint256 internal bid_stepMin; //5k, or 5%
    uint256 internal bid_cutOthers; // 500, or 0.5%

    uint256 internal bid_multiplier; //Will be divided by 100 for the calulations. 100 mean that doubling the bid mean 1% extra return

    /*
    ==================================================
                        Money section
    ==================================================
    */

    address internal publisher; //The address of the publisher of the cryptograph. Can edit media url and hash.
    address internal charity; //The address to which the chartity cut is being sent to
    address internal thirdParty; //The address of any third party taking a cut
    address internal perpertualAltruism; //The perpetual altruism address

    //The granularity of the redistribution is 0.001%. 100 000 = all the money
    uint256 internal publisherCut;
    uint256 internal charityCut;
    uint256 internal thirdPartyCut;
    uint256 internal perpetualAltruismCut;

    /*
    ==================================================
                        Timing section
    ==================================================
    */
    uint256 internal startTime; //The start date of the initial auction
    uint256 internal endTime; //The end date of the initial auction

    /*
    ==================================================
                        Binding section
    ==================================================
    */
    address internal auctionHouse; //The address of the auction house
    address internal myCryptograph; //The address of the Cryptograph I'm administrating
    address internal cryFactory; //The address of the cryptograph Factory

    bool internal initialized;

    //A mapping associating each bidder with their associated chainLink
    mapping (address => address) internal bidLinks;

    address internal initiator; //We keep the address of our initator for future minting

    uint256 internal numberOfBids; //Current number of standing bids
    uint256 internal maxSupply; //Maximum number of bid to keep
    address internal tailBidder; //The address of the current bottom bidder
}


/// @author Guillaume Gonnaud 2019
/// @title Minting Auction Storage Public
/// @notice Contain all the storage of the Minting Auction declared in a way that generate getters for Logic use
contract MintingAuctionStoragePublicV1 {
      /*
    ==================================================
                        Bidding section
    ==================================================
    */

    //The current bids made by each address
    mapping (address => uint) public currentBids;

    //The current amount of wei each address receive when outbid as the highest bid.
    mapping (address => uint) public duePayout; //How much the bidder make

    //The current highest bidder;
    address public highestBidder;

    //The current amount of unsettled payouts distributed for the current bidding process
    uint256 public unsettledPayouts;

    //The default starting price
    uint256 public startingPrice;

    //The current selling price
    uint256 public sellingPrice;

    /*
    ==================================================
                        Calculations section
    ==================================================
    */

    /*
    For a standing bid s and a new bid n, we express the return on the new bid as:

    incentive(n,s) % = min[ incmax , incmin + m * (n- s * (1+ stepmin)) / (s * (1+ stepmin))]

    Where:
    stepmin is the minimum bid increment, expressed as a fraction of the current standing bid (ex : 0.01 for 1/10 or 10%)
    incmin is the minimum incentive, expressed as a fraction
    incmax is the maximum incentive, expressed as a fraction
    m is the multiplier effect, expressed as a positive real number

    */

    //Values used to calculate the payouts.
    uint256 public bid_Decimals; //100k, or 100%
    uint256 public bid_incMax; //4.5k, or 4.5%
    uint256 public bid_incMin; //1k, or 1%
    uint256 public bid_stepMin; //5k, or 5%
    uint256 public bid_cutOthers; // 500, or 0.5%

    uint256 public bid_multiplier; //Will be divided by 100 for the calulations. 100 mean that doubling the bid mean 1% extra return

    /*
    ==================================================
                        Money section
    ==================================================
    */

    address public publisher; //The address of the publisher of the cryptograph. Can edit media url and hash.
    address public charity; //The address to which the chartity cut is being sent to
    address public thirdParty; //The address of any third party taking a cut
    address public perpertualAltruism; //The perpetual altruism address

    //The granularity of the redistribution is 0.001%. 100 000 = all the money
    uint256 public publisherCut;
    uint256 public charityCut;
    uint256 public thirdPartyCut;
    uint256 public perpetualAltruismCut;

    /*
    ==================================================
                        Timing section
    ==================================================
    */
    uint256 public startTime; //The start date of the initial auction
    uint256 public endTime; //The end date of the initial auction

    /*
    ==================================================
                        Binding section
    ==================================================
    */
    address public auctionHouse; //The address of the auction house
    address public myCryptograph; //The address of the Cryptograph I'm administrating
    address public cryFactory; //The address of the cryptograph Factory

    bool public initialized;

    //A mapping associating each bidder with their associated chainLink
    mapping (address => address) public bidLinks;

    address public initiator; //We keep the address of our initator for future minting

    uint256 public numberOfBids; //Current number of standing bids
    uint256 public maxSupply; //Maximum number of bids to keep
    address public tailBidder; //The address of the current bottom bidder

}


File 27 of 40: SenateLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./SenateV1.sol";
import "./CryptographIndexLogicV1.sol";
import "./TheCryptographLogicV1.sol";

/// @author Guillaume Gonnaud
/// @title Senate Logic Code
/// @notice This contract is used by the Version Control to determine what smart contracts are allowed to use in the ecosystem. It's logic code, cast a proxy with it.
contract SenateLogicV1 is VCProxyData, SenateHeaderV1, SenateStorageExternalV1  {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor() public
    {
        //Self intialize (nothing)
    }

    //Modifier for functions that requires to be called only by the lawmaker
    modifier restrictedToLawmaker(){
        require(msg.sender == lawmaker, "Only the lawmaker can call this function");
        _;
    }

    /// @notice Init function of the Senate
    /// @param _cryptographIndex The address of the cryptograph index that we will use to lookup owners
    function init(address _cryptographIndex) external {
        require (cryptographIndex == address(0x0), "The senate has already been initialized");
        cryptographIndex = _cryptographIndex;
    }

    /// @notice The Version Control must ask the senate (using this function) if an address is authorized before setting it in code
    /// @dev New addresses are voted in by cryptographs owners
    /// @param _candidateAddress The serial of the Cryptograph you want to peek highest bid on
    /// @return If the address can be used in the Version Control
    function isAddressAllowed(address _candidateAddress) external view returns(bool){
        bool retour = laws[_candidateAddress];
        return retour;
    }

    /// @notice Forcefully add or remove an address from the addresses the VC can use.
    /// @dev https://en.wikipedia.org/wiki/Article_49_of_the_French_Constitution
    /// @param _candidateAddress The serial of the Cryptograph you want to peek highest bid on
    /// @param _allowed A bool indicating _candidateAddress use for the Version Control. True if the address is to be allowed, False if disallowed
    function quaranteNeufTrois(address _candidateAddress, bool _allowed) external restrictedToLawmaker() {
        require(!democracy, "Democracy is enforced, new addresses must be subject to approval by the senate");
        laws[_candidateAddress] = _allowed;
        if(!_allowed){ //Are we deleting a law ?
                laws[_candidateAddress] = false;
                emit RemovedLogicCodeToVC(_candidateAddress); //emit the event
            } else { //We are adding a law
                laws[_candidateAddress] = true;
                emit AddedLogicCodeToVC(_candidateAddress); //emit the event
            }
    }

    /// @notice Set the senate to democratic : new laws must be voted upon
    function powerToThePeople() external restrictedToLawmaker(){
        require(!democracy, "Democracy is already in place");
        democracy = true; //https://youtu.be/T-TGPhVC0AE?t=11
        emit DemocracyOn(); //Emitting the event
    }

    /// @notice  Submit a new law proposition
    /// @param _law The proposed logic smart contract address to be added to the VC law pool
    /// @param _duration The time (in second) during which this law should be submitted to voting
    /// @param _revokeLaw Set to true if the law is to be removed instead of added to the pool
    /// @param _stateOfEmergency Set to true if democracy is to be revoked in the senate. Override _law and _revokeLaw.
    /// @return The index of the new law in the law index
    function submitNewLaw(address _law, uint256 _duration, bool _revokeLaw, bool _stateOfEmergency) external restrictedToLawmaker() returns (uint256) {
        lawPropositions.push(address(new LawProposition(_law, _duration, _revokeLaw, _stateOfEmergency)));
        emit NewLawProposal(lawPropositions.length - 1, _law, now + _duration, _revokeLaw, _stateOfEmergency);
        totalLaws = lawPropositions.length;
        return totalLaws;
    }

    /// @notice Vote on a law if you are a legitimate cryptograph owner
    /// @dev For now, only the first # of each edition has voting rights
    /// @param _vote True for agreeing with the law, false for refusing it
    /// @param _lawIndex The LawProposition index to be voted on
    /// @param _cryptographIssue The issue # of the Cryptograph
    /// @param _editionSerial The edition serial # of the Cryptograph.
    function VoteOnLaw(bool _vote, uint256 _lawIndex, uint256 _cryptographIssue, uint256 _editionSerial) external{

        require((_editionSerial == 1 || _editionSerial == 0 ), "Only the first serial of each edition is allowed to vote");

        //Grabbing the cryptograph
        address _cry = CryptographIndexLogicV1(cryptographIndex).getCryptograph(_cryptographIssue, true, _editionSerial);

        //Checking that you are indeed the cryptograph owner
        require(
            TheCryptographLogicV1(address(uint160(_cry))).owner() == msg.sender,
            "You are not an owner allowed to vote"
        );

        //Voting
        LawProposition(lawPropositions[_lawIndex]).vote(_vote, _cry);

        //Emitting the event
        emit Voted(_vote, _lawIndex, _cryptographIssue, _editionSerial);
    }

    /// @notice Enact a law
    /// @dev Will only work if past enaction time and positive votes >= negative votes
    /// @param _lawIndex The index of the LawProposition to be enacted
    function EnactLaw(uint256 _lawIndex) external restrictedToLawmaker(){
        //Grabbing the LawProposition
        LawProposition _lawProp = LawProposition(lawPropositions[_lawIndex]);
        _lawProp.enactable(); //Checks are made internally by the LawPropostion

        //int lawIndex, address law, uint256 enactionTime, bool revokeLaw, bool stateOfEmergency
        emit EnactProposal(_lawIndex, _lawProp.law(), _lawProp.enactionTime(), _lawProp.revokeLaw(), _lawProp.stateOfEmergency());

        if(_lawProp.stateOfEmergency()){ //Dying with thunderous applause
            democracy = false; //BETTER DEAD THAN RED
            emit DemocracyOff(); // I am the senate
        } else {
            if(_lawProp.revokeLaw()){ //Are we deleting a law ?
                laws[_lawProp.law()] = false;
                emit RemovedLogicCodeToVC(_lawProp.law()); //emit the event
            } else { //We are adding a law
                laws[_lawProp.law()] = true;
                emit AddedLogicCodeToVC(_lawProp.law()); //emit the event
            }
        }
    }

}


/// @author Guillaume Gonnaud
/// @title Auction House Logic Code
/// @notice Laws proposition instanced by the senate.
contract LawProposition {

    address public senate; //The address of the senate
    mapping (address => bool) public tokenWhoVoted; //A mapping storing every token that has voted on this law
    address public law; //The address of a smart contract logic code to be potentially used in the VC
    uint256 public enactionTime; //A timestamp storing the earliest time at which the lawmaker can enact the law
    bool public revokeLaw; //A bool true if the proposed smart contract address should be removed instead of added to the VC address pool
    bool public stateOfEmergency; //A boolean indicating whether or not democracy shall be revoked in the senate once this law passes
    uint256 public yesCount; //Number of tokens who voted yes
    uint256 public noCount; //Number of tokens who voted no

    modifier restrictedToSenate(){
        require((msg.sender == senate), "Only callable by senate/Can't vote anymore");
        _;
    }

    /// @notice Law constructor
    /// @dev This contract is meant to be used by the senate only
    /// @param _law The proposed logic smart contract address to be added to the VC law pool
    /// @param _duration The time (in second) during which this law should be submitted to voting
    /// @param _revokeLaw Set to true if the law is to be removed instead of added to the pool
    /// @param _stateOfEmergency Set to true if democracy is to be revoked in the senate. Override _law and _revokeLaw.
    constructor(address _law, uint256 _duration, bool _revokeLaw, bool _stateOfEmergency) public
    {
        //Duration of vote must be at least 24 hours
        require (_duration >= 60*60*24, "Voting should last at least 24 hours");
        require (_duration <= 60*60*24*366, "Voting should last maximum a year");

        //Setting the senate
        senate = msg.sender;

        //Setting the other params
        law = _law;
        revokeLaw = _revokeLaw;
        stateOfEmergency = _stateOfEmergency;
        enactionTime = now + _duration;

    }

    /// @notice Vote on a law
    /// @dev It is the senate responsability to ensure no imposters are voting
    /// @param _vote True if agreed with the law, false is against
    /// @param _token The address of the voting token
    function vote(bool _vote, address _token)  external restrictedToSenate(){

        //Checking if already voted
        require(!tokenWhoVoted[_token], "This token already cast a vote");

        //Setting up the vote
        tokenWhoVoted[_token] = true;

        //Counting the vote
        if(_vote){
            yesCount++;
        } else {
            noCount++;
        }

    }

    /// @notice Check if a law can be enacted. If yes, then prevents further voting.
    /// @dev Throw if not enactable (save gas)
    function enactable()  external restrictedToSenate(){
        require(enactionTime < now, "It is too early to enact the law");
        require(noCount <= yesCount, "Too many voters oppose the law");
        senate = address(0x0); //Disable voting/enacting laws
    }


}

File 28 of 40: SenateProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./SenateV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Senate Proxy Smart Contract
/// @notice The Senate proxy : this is this contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract SenateProxiedV1 is VCProxy, SenateHeaderV1, SenateStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that we know where our logic code is
    {
        lawmaker = msg.sender; //Only the creator of this smart contract will be able to submit new addresses to be voted on
    }

    //No other logic code as it is all proxied

}



File 29 of 40: SenateV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Senate Header
/// @notice Contain all the events emitted by the Senate
contract SenateHeaderV1 {
    event AddedLogicCodeToVC(address law); //A new logic code address (law) was added to the available pool
    event RemovedLogicCodeToVC(address law); //A logic code address (law) was removed from the available pool
    event NewLawProposal(uint256 lawIndex, address law, uint256 enactionTime, bool revokeLaw, bool stateOfEmergency); //A new law proposal to vote on
    event EnactProposal(uint256 lawIndex, address law, uint256 enactionTime, bool revokeLaw, bool stateOfEmergency); //The lawmaker applied a proposal
    event Voted(bool vote, uint256 lawIndex, uint256 issueNumber, uint256 SerialNumber); //Emitted when a token holder vote on a low
    event DemocracyOn(); //Enable voting before adding new laws
    event DemocracyOff(); //Give back the ability to the lawmaker to add any law without a vote
}


/// @author Guillaume Gonnaud
/// @title Senate Storage Internal
/// @notice Contain all the storage of the Senate declared in a way that does not generate getters for Proxy use
contract SenateStorageInternalV1 {

    bool internal democracy; //A bool controlling if address addition/removal is subject to vote
    mapping (address => bool) internal laws; //The list of allowed smart contract addresses for use in the Version Control
    address internal lawmaker; //Address allowed to sumbmit new address to be voted upon.
    address[] internal lawPropositions; //List of proposed laws to be voted upon
    address internal cryptographIndex; //The cryptograph index address
    uint256 internal totalLaws; //The total number of published laws

}


/// @author Guillaume Gonnaud
/// @title Senate Storage External
/// @notice Contain all the storage of the Senate declared in a way that generates getters for Logic Code use
contract SenateStorageExternalV1 {

    bool public democracy; //A bool controlling if address addition/removal is subject to vote
    mapping(address => bool) public laws; //The list of allowed smart contract addresses for use in the VC
    address public lawmaker; //Address allowed to sumbmit new address to be voted upon.
    address[] public lawPropositions; //List of proposed laws to be voted upon
    address public cryptographIndex; //The cryptograph index address
    uint256 public totalLaws; //The total number of published laws
}


File 30 of 40: SingleAuctionBidLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./SingleAuctionV1.sol";
import "./CryptographFactoryV1.sol";
import "./AuctionHouseLogicV1.sol";
import "./TheCryptographLogicV1.sol";
import "./CryptographInitiator.sol";
import "./BidLinkSimple.sol";


/// @author Guillaume Gonnaud 2019
/// @title Single Auction Bid Logic Code
/// @notice Implements a GBM auction bid function. See white paper for the details. Logic code, to be casted on a proxy.
contract SingleAuctionBidLogicV1 is VCProxyData, SingleAuctionHeaderV1, SingleAuctionStorageInternalV1  {

    //Modifier for functions that requires to be called only by the Auction house
    modifier restrictedToAuctionHouse(){
        require((msg.sender == auctionHouse), "Only the auction house smart contract can call this function");
        _;
    }


    /// @notice Place a bid to own a cryptograph and distribute the incentives
    /// @dev Only callable by the Auction House
    /// @param _newBidAmount The amount of the new bid
    /// @param _newBidder The address of the bidder
    function bid(uint256 _newBidAmount, address _newBidder) external payable restrictedToAuctionHouse(){

        /*
        ========================== money check ==========================
        */

        //uninitialized  cryptographs can't be bid upon
        require(initialized, "This auction has not been properly setup yet");

        //Did we send the proper amount of money ?
        require(_newBidAmount == msg.value + currentBids[_newBidder], "Amount of money sent incorrect");

        //check to be made : is the new bid big enough ?
        require( //Either fresh bid or meeting the standing bid * the step
            ((highestBidder == address(0)) && startingPrice <= _newBidAmount) ||
            ( (highestBidder != address(0)) && (currentBids[highestBidder] * (bid_Decimals + bid_stepMin) <= (_newBidAmount * bid_Decimals) )),
            "New bid amount does not meet the minimal new bid amount");

        /*
        ========================== Timing check ==========================
        */

        //We must be past the initial auction start
        require(now >= startTime, "You can only bid once the initial auction has started");

        //Checking if an auction is not over
        require((now < endTime && TheCryptographLogicV1(myCryptograph).owner() == address(0x0)) ||
        (TheCryptographLogicV1(myCryptograph).owner() != address(0x0) && (hammerTime == 0 || now < hammerTime)),
        "The auction is over, the bid was rejected");

        //Extending the time at the end of the initial auction
        if(endTime < now + 600 && TheCryptographLogicV1(myCryptograph).owner() == address(0x0)){
            endTime = now + 600;
        }

        //If hammer time is non-zero, we must be before the end of hammerTime
        //This allow potential bidders to come in.
        if(hammerTime != 0 && now + 600 > hammerTime){
            hammerTime = now + 600; //Extend the hammertime auction by 600s
            hammerBlock = block.number + 4; //Extend the number of minimum elapsed block by 4
        }


        //Emit the bid acceptance event before triggering the payouts
        emit BidAccepted(_newBidAmount, _newBidder);

        /*
        ========================== Payouts ==========================
        */

        //0.5% of the bid is sent to third parties
        uint256 duePay;

        //The first bid in perpetual trading is exempted from bidding fees
        if(!(highestBidder == address(0) && TheCryptographLogicV1(myCryptograph).owner() != address(0))){
            //If not, a bidding fee is taken and distrubuted
            duePay = (_newBidAmount * bid_cutOthers)/bid_Decimals;
            unsettledPayouts += duePay;
            distributeStakeholdersPayouts(duePay, _newBidder);
        }

        //Send his payout to the previous highest bidder
        duePay = duePayout[highestBidder];
        if(duePay != 0){
            unsettledPayouts += duePay;
            emit Payout(duePay,  highestBidder,  _newBidder);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: duePay }(highestBidder, _newBidder);
        }

        /*
        ========================== Reward ==========================
        */

        //Set the new payout amount we will receive when outbid
        calculateReward(_newBidAmount, _newBidder);

        /*
        ===================== Bid Cancellation =====================
        */
        uint256 toSend;

        if ( hammerTime != 0 || TheCryptographLogicV1(myCryptograph).owner() == address(0)) { //Ongoing sale
            
            if(highestBidder != _newBidder){
                //We cancel and withdraw the current highest standing bid
                toSend = currentBids[highestBidder];
                if(toSend != 0){ //For the case of the first ever bid on an auction : address 0x0 is not cancelling anything...
                    //Send back all the money : no payout settlement required
                    emit BidCancelled(toSend, toSend, highestBidder);
                    AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(highestBidder, highestBidder);
                    //Edge case because of renatus : we may have a link of bids to maintain, so no reseting links
                    delete currentBids[highestBidder];
                    delete duePayout[highestBidder];
                }
            }
        }

            
        if(currentBids[_newBidder] != 0){
            emit BidCancelled(currentBids[_newBidder], 0, _newBidder);
            //No need to send any back money to a self outbidder : this smart contract only receive the extra required amount

            //Updating our neigbors link
            if( BidLinkSimple(bidLinks[_newBidder]).above() != address(0x0)){
                  BidLinkSimple(BidLinkSimple(bidLinks[_newBidder]).above()).setBelow(BidLinkSimple(bidLinks[_newBidder]).below()); //Unlinking above us
            }

            //We don't need to update the link below us if we are already the highest bidder
            if(highestBidder != _newBidder){
                if( BidLinkSimple(bidLinks[_newBidder]).below() != address(0x0)){
                    BidLinkSimple(BidLinkSimple(bidLinks[_newBidder]).below()).setAbove(BidLinkSimple(bidLinks[_newBidder]).above()); //Unlinking below us
                }
            }
        }
        

        /*
        ===================== Bid Registering =====================
        */

        //Check if the bidder already had a link. Create one if not
        if(bidLinks[_newBidder] == address(0x0)){
            bidLinks[_newBidder] = address(new BidLinkSimple(_newBidder));
        }

        //Link our bidlink to the previous head, if it's not already us (which would mean no changes)
        if(_newBidder != highestBidder){

            //There is no one above us
            BidLinkSimple(bidLinks[_newBidder]).setAbove(address(0x0));

            if( hammerTime == 0 && TheCryptographLogicV1(myCryptograph).owner() != address(0)){ //We did not cancel the previous highest bidder
                //The Link below us is the previous highest bidder
                BidLinkSimple(bidLinks[_newBidder]).setBelow(bidLinks[highestBidder]);

                //We are above the link below us
                if(highestBidder != address(0x0)){
                    BidLinkSimple(bidLinks[highestBidder]).setAbove(bidLinks[_newBidder]);
                }

            } else { //We just cancelled the previous highest bidder

                //If said highest bidder existed
                if(highestBidder != address(0x0)){

                    //The bid below us is the bid that is currently below the still registered previous highest bidder
                    BidLinkSimple(bidLinks[_newBidder]).setBelow(BidLinkSimple(bidLinks[highestBidder]).below());

                    //We are also above said bid
                    if(BidLinkSimple(bidLinks[highestBidder]).below() != address(0x0)){
                       BidLinkSimple(BidLinkSimple(bidLinks[highestBidder]).below()).setAbove(bidLinks[_newBidder]);
                    }


                } else {
                    //There is no one below us
                    BidLinkSimple(bidLinks[_newBidder]).setBelow(address(0x0));
                }
            }
        }

        //Set the amount of the new highest bid
        currentBids[_newBidder] = _newBidAmount;
        highestBidder = _newBidder; //We are the new highest bidder

        /*
        ===================== Sales Trigger =====================
        */

        //If a selling price have been met, trigger a sale
        if( sellingPrice != 0 && _newBidAmount >= sellingPrice && hammerTime == 0){
            hammerTime = now + hammerTimeDuration;
            hammerBlock = block.number + hammerBlockDuration;
            emit SaleStarted(_newBidder, hammerTime, hammerBlock);
        }
    }

    /// @notice Function used to distribute an arbitrary amount of money among non-bidders
    /// @dev Only callable internally
    /// @param _amount The amount of money to spread
    /// @param _contributor The address of the source of the money
    function distributeStakeholdersPayouts(uint256 _amount, address _contributor) internal{
        uint256 toDistribute = _amount;
        uint256 toSend;

        //Pay the charity
        toSend = (charityCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  charity,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(charity, _contributor);
        }

        //Pay the publisher
        toSend = (publisherCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  publisher,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(publisher, _contributor);
        }

        //Pay the thirdParty account
        toSend = (thirdPartyCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  thirdParty,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(thirdParty, _contributor);
        }

        //Pay perpetual Altruism the reminder (25%). only non-null guaranteed address, so send any rounding errors there
        toSend = toDistribute;
        if(toSend != 0){
            emit Payout(toSend,  perpertualAltruism,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(perpertualAltruism, _contributor);
        }
    }

    /// @notice Calculating and setting how much payout a bidder will receive if outbid
    /// @dev Only callable internally
    /// @param _newBid The amount of money in the new bid
    /// @param _bidder The address of the new bidder
    function calculateReward(uint256 _newBid, address _bidder) internal{

        //Calculating how much payout we will receive if we are outbid

        //Init the baseline bid we need to perform against
        uint256 baseBid = currentBids[highestBidder] * (bid_Decimals + bid_stepMin) / bid_Decimals;
        if(baseBid == 0){
            baseBid = startingPrice;

            //Do not divide by 0
            if(baseBid == 0){
                baseBid = 1;
            }
        }

        //We calculate our baseline reward. We square the decimals to guarantee a granularity of at least 1/bid_Decimals instead of 1/bid_multiplier
        //This also somewhat limit the hardcap for a reward to max_UInt256/10^11 => Not a problem as this amount of eth will not be minted
        uint256 decimaledRatio = ((bid_Decimals * bid_multiplier * (_newBid - baseBid) ) / baseBid) + bid_incMin * bid_Decimals;

        //If we go over the maximum payout, we set the reward to the maximum payout
        if(decimaledRatio > (bid_Decimals * bid_incMax)){
            decimaledRatio = bid_Decimals * bid_incMax;
        }

        duePayout[_bidder] = (_newBid * decimaledRatio)/(bid_Decimals*bid_Decimals);
    }

}


File 31 of 40: SingleAuctionLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./SingleAuctionV1.sol";
import "./CryptographFactoryV1.sol";
import "./AuctionHouseLogicV1.sol";
import "./TheCryptographLogicV1.sol";
import "./CryptographInitiator.sol";
import "./BidLinkSimple.sol";


/// @author Guillaume Gonnaud 2019
/// @title Single Auction Logic Code
/// @notice Implements a GBM auction. See white paper for the details. Logic code, to be casted on a proxy.
contract SingleAuctionLogicV1 is VCProxyData, SingleAuctionHeaderV1, SingleAuctionStoragePublicV1  {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor () public
    {
        //Self intialize (nothing)
    }

    //Modifier for functions that requires to be called only by the Auction house
    modifier restrictedToAuctionHouse(){
        require((msg.sender == auctionHouse), "Only the auction house smart contract can call this function");
        _;
    }

    /// @notice Init function of the MintingAuction
    /// @param _myCryptograph The address of the cryptograph this auction is paired with
    /// @param _cryInitiator The address of the initator containing the details of our auction
    /// @param _initialize true => can't change any spec afterward. false => can initialize again.
    function initAuction(
            address _myCryptograph,
            address _cryInitiator,
            bool _initialize
        ) public {

        require(!initialized, "This auction is already initialized");
        initialized = _initialize; //Are we locking ?

        //we must be either perpetual altruism OR never inited before
        require(auctionHouse == address(0) || msg.sender == cryFactory,"Only Perpetual altruism can change a yet to be locked auction");
        cryFactory = msg.sender;

        /*
        ==================================================
                            Bidding section
        ==================================================
        */

        startingPrice = CryptographInitiator(_cryInitiator).startingPrice(); //The first bid that need to be outbid is 1 Wei
        sellingPrice = 0; //A newly minted cryptograph doesn't have an owner willing to sell

        /*
        ==================================================
                        Calculations section
        ==================================================
        */
        bid_Decimals = 100000;  //100k, or 100%
        bid_incMax = 10000; //10k, or 10%
        bid_incMin = 1000; //1k, or 1%
        bid_stepMin = 10500; //10.5k, or 10.5%
        bid_cutOthers = 500; // 500, or 0.5%
        bid_multiplier = 11120; // 9000 = Doubling step min bid yield max gain (1%+9% = 10%). 

        sale_fee = 10000; //10k, or 10%

         /*
        ==================================================
                            Money section
        ==================================================
        */
        //Setting up money flow
        perpertualAltruism = CryptographFactoryStoragePublicV1(cryFactory).officialPublisher();
        perpetualAltruismCut = CryptographInitiator(_cryInitiator).perpetualAltruismCut();
        publisher = CryptographInitiator(_cryInitiator).publisher();
        publisherCut = CryptographInitiator(_cryInitiator).publisherCut();
        charity = CryptographInitiator(_cryInitiator).charity();
        charityCut = CryptographInitiator(_cryInitiator).charityCut();
        thirdParty = CryptographInitiator(_cryInitiator).thirdParty();
        thirdPartyCut = CryptographInitiator(_cryInitiator).thirdPartyCut();

        //Setting up timings
        startTime = CryptographInitiator(_cryInitiator).auctionStartTime();
        endTime = CryptographInitiator(_cryInitiator).auctionStartTime() + CryptographInitiator(_cryInitiator).auctionSecondsDuration();

        hammerBlockDuration = 10; //Minimum 10 blocks
        hammerTimeDuration = 36*60*60; //The new perpetual auction will last for 36 hours at least
        delete hammerBlock;
        delete hammerTime;

        auctionHouse = CryptographFactoryStoragePublicV1(cryFactory).targetAuctionHouse();
        myCryptograph = _myCryptograph;
    }

    /// @notice Make an official auction unmodifiable once we are certain the parameters are correct
    /// @dev Only callable by perpetual altruism
    function lock() external{
        require(msg.sender == cryFactory, "Only Perpetual altruism can lock the initialization");
        initialized = true;
    }

    /// @notice Place a bid to own a cryptograph and distribute the incentives
    /// @dev Only callable by the Auction House
    /// @param _newBidAmount The amount of the new bid
    /// @param _newBidder The address of the bidder
    function bid(uint256 _newBidAmount, address _newBidder) external payable restrictedToAuctionHouse(){
        //Empty, as we are supposed to execute SingleAuctionBidLogic bid function but better have this function in the ABI
    }

    /// @notice Cancel a bid placed previously by a bidder
    /// @dev Only callable by the Auction House
    /// @param _bidder The address of the bidder wanting to cancel his bid
    function cancelBid(address _bidder) external restrictedToAuctionHouse(){

        //We can only cancel existing bids
        require(currentBids[_bidder] != 0, "Can't cancel a bid that does not exist");

        //We can only cancel past the initial auction
        require(TheCryptographLogicV1(myCryptograph).owner() != address(0), "Bids cannot be manually cancelled during the initial auction");

        //We can't cancel during hammerTime if we are the highest bidder
        require(hammerTime == 0 || _bidder != highestBidder, "The highest bid cannot be cancelled once a seller accepted a sale");

        uint256 toSend = currentBids[_bidder];

        //If we are the highest bidder we have to settle the payouts before cancelling
        //unsettledPayouts = 0 for the highest bidder if either first bidder or someone cancelled above
        if(_bidder == highestBidder ){
            //Deduce from amount of money we get back the unsettled payout
            toSend -= unsettledPayouts;
            unsettledPayouts = 0;

            //Finding the new highest bidder :
            //Explore the below link of the highest bidder. (We are cancelling a bid, so a highest bidder exist)
            address _linkHighest = BidLinkSimple(bidLinks[_bidder]).below();
            //If the below link exist, then the associated bidder is the next highest bidder
            if(_linkHighest != address(0x0)){
                highestBidder = BidLinkSimple(_linkHighest).bidder();
            } else {
                //No more bidders : we are cancelling ourselves
                delete highestBidder;
            }
        }

        //Emit the cancellation event
        emit BidCancelled(currentBids[_bidder], toSend, _bidder);

        //Reset our bid related variables
        currentBids[_bidder] = 0;
        duePayout[_bidder] = 0;


        //Updating our neigbors link
        if( BidLinkSimple(bidLinks[_bidder]).above() != address(0x0)){
            BidLinkSimple(BidLinkSimple(bidLinks[_bidder]).above()).setBelow(BidLinkSimple(bidLinks[_bidder]).below()); //Unlinking above us
        }

        if( BidLinkSimple(bidLinks[_bidder]).below() != address(0x0)){
            BidLinkSimple(BidLinkSimple(bidLinks[_bidder]).below()).setAbove(BidLinkSimple(bidLinks[_bidder]).above()); //Unlinking below us
        }

        //Finally, we send the funds back to the auction house
        AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(_bidder, _bidder);

    }

    /// @notice Set an instant sale price. If set to 0, instant sale can't be triggered.
    /// @dev Only callable by the Auction House. Can't be cancelled
    /// @param _seller The address of the owner wishing to sell the Cryptograph
    /// @param _sellPrice The minimum amount of eth the seller wants to get
    function setSellingPrice(address _seller, uint256 _sellPrice) external restrictedToAuctionHouse(){

        require(!isBeingERC2665Approved, "You can't auction a cryptograph that a third party can reclaim");

        require(_seller == TheCryptographLogicV1(myCryptograph).owner(), "The seller can only be the owner");
        require(hammerTime == 0, "A sale is already in progress");

        sellingPrice = _sellPrice;

        emit SellingPriceAdjusted(_seller, _sellPrice);

        if(currentBids[highestBidder] >= _sellPrice && _sellPrice != 0){ //Start a sale if the selling price is already met by the highest bidder
            hammerTime = now + hammerTimeDuration;
            hammerBlock = block.number + hammerBlockDuration;
            emit SaleStarted(_seller, hammerTime, hammerBlock);
        }

        //Resetting Renatus timer
        TheCryptographLogicV1(myCryptograph).renatus();

    }

    /// @notice Assign the cryptograph to its new legitmate owner. Only callable after the initial Auction or HammerTime
    /// @dev Only callable by the Auction House.
    /// @param _newOwner The address of the bidder wishing to win the cryptograph
    /// @return 0 if a normal auction, 1 if a minting auction
    function win(address _newOwner) external restrictedToAuctionHouse() returns(uint){

        //Only the highest bidder can win a cryptograph
        require(_newOwner == highestBidder, "Only the highest bidder can win the Cryptograph");
     
        //startingPrice being reset to 1 wei
        if(startingPrice != 1){
            startingPrice = 1;
        }

        //Fire the transfer following a win event
        emit Win(_newOwner, TheCryptographLogicV1(myCryptograph).owner(), currentBids[highestBidder]);

        uint256 toSend;

        //If there is no owner yet
        if(TheCryptographLogicV1(myCryptograph).owner() == address(0)){
            //We are in the initial sale process
            require(now > endTime, "The initial auction is not over yet");

            //All the proceeds of the sale are distributed to third parties
            distributeStakeholdersPayouts(currentBids[highestBidder] - unsettledPayouts, _newOwner);

        } else {
            //We are in the perpetual sale process

            //A sale must be happening and other bidders must have had an opportunity to place their own bids
            require(hammerTime != 0, "No sales are happening right now");
            require(now > hammerTime, "Not enough time has elapsed since the seller accepted the sale");
            require(block.number > hammerBlock, "Not enough blocks have been mined since the seller accepted the sale");

            delete hammerBlock; //Reset the minimal sale block
            delete hammerTime; //Reset the minmimal sale time

            //10% of the seller proceed is distributed to third parties
            toSend = ((currentBids[highestBidder] - unsettledPayouts ) * sale_fee) / bid_Decimals;
            distributeStakeholdersPayouts(toSend, _newOwner);

            //The remainder of the money is then sent to the seller
            toSend = currentBids[highestBidder] - unsettledPayouts - toSend;
            emit Payout(toSend, TheCryptographLogicV1(myCryptograph).owner(), _newOwner);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(TheCryptographLogicV1(myCryptograph).owner(), _newOwner);
        }

        delete unsettledPayouts; //Reset the payouts

        /*
            Find the new highest bidder
         */

        //Reset our bid related variables
        currentBids[_newOwner] = 0;
        duePayout[_newOwner] = 0;

        //Finding the new highest bidder :
        //Explore the below link of the highest bidder.
        address _linkHighest = BidLinkSimple(bidLinks[highestBidder]).below();

        //Deleting  our link
        delete bidLinks[highestBidder];

        //If the below link exist, then the associated bidder is the next highest bidder
        if(_linkHighest != address(0x0)){
            highestBidder = BidLinkSimple(_linkHighest).bidder();
            BidLinkSimple(_linkHighest).setAbove(address(0x0)); //Our below neighbor is the new highest bidder
        } else {
            //No more bidders : we are cancelling ourselves
            delete highestBidder;
        }


        //Reset the selling price
        sellingPrice = 0;
        emit SellingPriceAdjusted(_newOwner, 0);

        //Actually transfer the cryptograph
        TheCryptographLogicV1(myCryptograph).transfer(_newOwner);

        return 0;
    }

    /// @notice Function used to distribute an arbitrary amount of money among non-bidders
    /// @dev Only callable internally
    /// @param _amount The amount of money to spread
    /// @param _contributor The address of the source of the money
    function distributeStakeholdersPayouts(uint256 _amount, address _contributor) internal{
        uint256 toDistribute = _amount;
        uint256 toSend;

        //Pay the charity
        toSend = (charityCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  charity,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(charity, _contributor);
        }

        //Pay the publisher
        toSend = (publisherCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  publisher,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(publisher, _contributor);
        }

        //Pay the thirdParty account
        toSend = (thirdPartyCut * _amount) / bid_Decimals;
        toDistribute -= toSend;
        if(toSend != 0){
            emit Payout(toSend,  thirdParty,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(thirdParty, _contributor);
        }

        //Pay perpetual Altruism the reminder (25%). only non-null guaranteed address, so send any rounding errors there
        toSend = toDistribute;
        if(toSend != 0){
            emit Payout(toSend,  perpertualAltruism,  _contributor);
            AuctionHouseLogicV1(address(uint160(auctionHouse))).addFundsFor{value: toSend }(perpertualAltruism, _contributor);
        }
    }

    /// @notice Calculating and setting how much payout a bidder will receive if outbid
    /// @dev Only callable internally
    /// @param _newBid The amount of money in the new bid
    /// @param _bidder The address of the new bidder
    function calculateReward(uint256 _newBid, address _bidder) internal{

        //Calculating how much payout we will receive if we are outbid

        //Init the baseline bid we need to perform against
        uint256 baseBid = currentBids[highestBidder] * (bid_Decimals + bid_stepMin) / bid_Decimals;
        if(baseBid == 0){
            baseBid = startingPrice;

            //Do not divide by 0
            if(baseBid == 0){
                baseBid = 1;
            }
        }

        //We calculate our baseline reward. We square the decimals to guarantee a granularity of at least 1/bid_Decimals instead of 1/bid_multiplier
        //This also somewhat limit the hardcap for a reward to max_UInt256/10^11 => Not a problem as this amount of eth will not be minted
        uint256 decimaledRatio = ((bid_Decimals * bid_multiplier * (_newBid - baseBid) ) / baseBid) + bid_incMin * bid_Decimals;

        //If we go over the maximum payout, we set the reward to the maximum payout
        if(decimaledRatio > (bid_Decimals * bid_incMax)){
            decimaledRatio = bid_Decimals * bid_incMax;
        }

        duePayout[_bidder] = (_newBid * decimaledRatio)/(bid_Decimals*bid_Decimals);
    }

    /// @notice resetting an auction starting in two weeks with the initial auction parameters. No changes to the existing bids.
    /// @dev Only callable by our own cryptograph
    function renatus() external{

        require(msg.sender == myCryptograph, "Only callable by the paired Cryptograph");

        delete hammerBlock; //Reset the minimal sale block
        delete hammerTime; //Reset the minmimal sale time
        delete sellingPrice; //Reset the selling price

        //Reset the auction end/start time to be same duration as initial auction but starting in 14 days
        endTime = now + 60*60*24*14 + endTime - startTime;
        startTime = now + 60*60*24*14;

        //Actually transfer the cryptograph 
        TheCryptographLogicV1(myCryptograph).transfer(address(0));
    }

    /// @notice transfer following the ERC2665 standard
    /// @dev Only callable by the auction house
    /// @param _contributor The operator paying the transfer fee
    /// @param _to The address of the new owner
    function transferERC2665(address _contributor, address _to) external payable restrictedToAuctionHouse() {

         if(msg.value != 0){
            //Distributing the transfer fee
            distributeStakeholdersPayouts(msg.value, _contributor);
        }

        //Checking that no auctions are running
        require(hammerTime == 0, "Can't transfer a cryptograph under sale");
   
        //Reset the selling price
        if(sellingPrice != 0){
            sellingPrice = 0;
            emit SellingPriceAdjusted(_contributor, 0);
        }

        //Actually transfer the cryptograph
        TheCryptographLogicV1(myCryptograph).transfer(_to);

        //New owner mean no approval
        isBeingERC2665Approved = false;

    }

    
    /// @notice Approve following the ERC2665
    /// @dev Only callable by the auction house
    /// @param _contributor The operator paying the transfer fee
    /// @param _approvedAddress The address of the new approved address
    function approveERC2665(address _contributor, address _approvedAddress) external payable restrictedToAuctionHouse(){

        if(msg.value != 0){
            //Distributing the transfer fee
            distributeStakeholdersPayouts(msg.value, _contributor);
        }
      

        //Checking that no auctions are running
        require(hammerTime == 0, "Can't approve a cryptograph under sale");

        //Reset the selling price
        if(sellingPrice != 0){
            sellingPrice = 0;
            emit SellingPriceAdjusted(_contributor, 0);
        }
        
        //Checking address approval
        if(_approvedAddress == address(0) || _approvedAddress == TheCryptographLogicV1(myCryptograph).owner()){
            isBeingERC2665Approved = false;
        } else {
            isBeingERC2665Approved = true;
        }

    }

}


File 32 of 40: SingleAuctionProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./SingleAuctionV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  Single Auction Proxy Smart Contract
/// @notice The Single Auction proxy : this is the contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract SingleAuctionProxiedV1 is VCProxy, SingleAuctionHeaderV1, SingleAuctionStorageInternalV1  {

    constructor(uint256 _version, address _vc, uint256 _versionBid)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that we know where our logic code is
    {
        versionBid = _versionBid;
    }

    //Routing the bid function to a separate smart contract than the regular version, with proper Bid ABI
    function bid(uint256 , address) external payable {

        address addr = VersionControlStoragePublic(vc).code(versionBid);
        assembly {
            let freememstart := mload(0x40)
            calldatacopy(freememstart, 0, calldatasize())
            let success := delegatecall(not(0), addr, freememstart, calldatasize(), freememstart, 0)
            returndatacopy(freememstart, 0, returndatasize())
            switch success
            case 0 { revert(freememstart, returndatasize()) }
            default { return(freememstart, returndatasize()) }
        }
    }


}



File 33 of 40: SingleAuctionV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/// @author Guillaume Gonnaud 2019
/// @title Single Auction Header
/// @notice Contain all the events emitted by the Single Auction
contract SingleAuctionHeaderV1 {
    event BidAccepted(uint256 bidValue, address indexed bidder);
    event Payout(uint256 amount, address indexed beneficiary, address indexed contributor);
    event BidCancelled(uint256 bidValue, uint256 ethReturned, address indexed bidder);
    event SaleStarted(address indexed seller, uint256 hammerTime, uint256 hammerBlock);
    event SellingPriceAdjusted(address indexed seller, uint256 amount);
    event Win(address indexed buyer, address indexed seller, uint256 bidValue);
}


/// @author Guillaume Gonnaud 2019
/// @title Single Auction Storage Internal
/// @notice Contain all the storage of the Single Auction declared in a way that does not generate getters for Proxy use
contract SingleAuctionStorageInternalV1 {

    //Used to store the index number of the bidding logic contract
    uint256 internal versionBid;

    /*
    ==================================================
                        Bidding section
    ==================================================
    */

    //The current bids made by each address
    mapping (address => uint) internal currentBids;

    //The current amount of wei each address receive when outbid as the highest bid.
    mapping (address => uint) internal duePayout; //How much the bidder make

    //The current highest bidder;
    address internal highestBidder;

    //The current amount of unsettled payouts distributed for the current bidding process
    uint256 internal unsettledPayouts;

    //The default starting price
    uint256 internal startingPrice;

    //The current selling price
    uint256 internal sellingPrice;

    //A mapping associating each bidder with their associated chainLink
    mapping (address => address) internal bidLinks;

    /*
    ==================================================
                        Calculations section
    ==================================================
    */

    /*
    For a standing bid s and a new bid n, we express the return on the new bid as:

    incentive(n,s) % = min[ incmax , incmin + m * (n- s * (1+ stepmin)) / (s * (1+ stepmin))]

    Where:
    stepmin is the minimum bid increment, expressed as a fraction of the current standing bid (ex : 0.01 for 1/10 or 10%)
    incmin is the minimum incentive, expressed as a fraction
    incmax is the maximum incentive, expressed as a fraction
    m is the multiplier effect, expressed as a positive real number

    */

    //Values used to calculate the payouts.
    uint256 internal bid_Decimals; //100k, or 100%
    uint256 internal bid_incMax; //10k, or 10%
    uint256 internal bid_incMin; //1k, or 1%
    uint256 internal bid_stepMin; // 10.5k, or 10.5%
    uint256 internal bid_cutOthers; // 500, or 0.5%

    uint256 internal bid_multiplier; //Will be divided by 100 for the calulations. 100 means that doubling the bid leads to 1% extra return

    uint256 internal sale_fee; //Proportion of the bid_Decimals taken as a selling fee. 10% = 10k


    /*
    ==================================================
                        Money section
    ==================================================
    */

    address internal publisher; //The address of the publisher of the cryptograph. Can edit media url and hash.
    address internal charity; //The address to which the chartity cut is being sent to. No special rights.
    address internal thirdParty; //The address of any third party taking a cut. No special rights.
    //The perpetual altruism address. Always take 25%+ for community cryptographs. Same as publisher for official cryptographs.
    address internal perpertualAltruism;

    //The granularity of the redistribution is 0.001%. 100 000 = all the money
    uint256 internal publisherCut;
    uint256 internal charityCut;
    uint256 internal thirdPartyCut;
    uint256 internal perpetualAltruismCut;

    /*
    ==================================================
                        Timing section
    ==================================================
    */
    uint256 internal startTime; //The start date of the initial auction
    uint256 internal endTime; //The end date of the initial auction

    uint256 internal hammerBlockDuration; //The minium number of blocks for which other bidder can come in after a winning offer
    uint256 internal hammerTimeDuration; //The  number of seconds for which other bidder can come in after a winning offer
    uint256 internal hammerBlock; //The block number after which a winning offer can claim a cryptograph
    uint256 internal hammerTime; //The date after which a winning offer can claim a cryptograph

    /*
    ==================================================
                        Binding section
    ==================================================
    */
    address internal auctionHouse; //The address of the auction house
    address internal myCryptograph; //The address of the Cryptograph I'm administrating
    address internal cryFactory; //The address of the cryptograph Factory

    bool internal initialized;
    bool internal isBeingERC2665Approved; //If set to true, a potential new owner has been approved in ERC2665

}


/// @author Guillaume Gonnaud 2019
/// @title Single Auction Storage Public
/// @notice Contain all the storage of the Single Auction declared in a way that generates getters for Logic use
contract SingleAuctionStoragePublicV1 {

    //Used to store the VC index number of the bidding logic conctract
    uint256 internal versionBid;

    /*
    ==================================================
                        Bidding section
    ==================================================
    */

    //The current bids made by each address
    mapping (address => uint) public currentBids;

    //The current amount of wei each address receive when outbid as the highest bid.
    mapping (address => uint) public duePayout; //How much the bidder make

    //The current highest bidder;
    address public highestBidder;

    //The current amount of unsettled payouts distributed for the current bidding process
    uint256 public unsettledPayouts;

    //The default starting price
    uint256 public startingPrice;

    //The current selling price
    uint256 public sellingPrice;

    //A mapping associating each bidder with their associated chainLink
    mapping (address => address) public bidLinks;

    /*
    ==================================================
                        Calculations section
    ==================================================
    */

    /*
    For a standing bid s and a new bid n, we express the return on the new bid as:

    incentive(n,s) % = min[ incmax , incmin + m * (n- s * (1+ stepmin)) / (s * (1+ stepmin))]

    Where:
    stepmin is the minimum bid increment, expressed as a fraction of the current standing bid (ex : 0.01 for 1/10 or 10%)
    incmin is the minimum incentive, expressed as a fraction
    incmax is the maximum incentive, expressed as a fraction
    m is the multiplier effect, expressed as a positive real number

    */

    //Values used to calculate the payouts.
    uint256 public bid_Decimals; //100k, or 100%
    uint256 public bid_incMax; //10k, or 10%
    uint256 public bid_incMin; //1k, or 1%
    uint256 public bid_stepMin; // 10.5k, or 10.5%
    uint256 public bid_cutOthers; // 500, or 0.5%

    uint256 public bid_multiplier; //Will be divided by 100 for the calulations. 100 mean that doubling the bid mean 1% extra return

    uint256 public sale_fee; //Proportion of the bid_Decimals taken as a selling fee. 10% = 10k

    /*
    ==================================================
                        Money section
    ==================================================
    */

    address public publisher; //The address of the publisher of the cryptograph. Can edit media url and hash.
    address public charity; //The address to which the chartity cut is being sent to
    address public thirdParty; //The address of any third party taking a cut
    address public perpertualAltruism; //The perpetual altruism address

    //The granularity of the redistribution is 0.001%. 100 000 = all the money
    uint256 public publisherCut;
    uint256 public charityCut;
    uint256 public thirdPartyCut;
    uint256 public perpetualAltruismCut;

    /*
    ==================================================
                        Timing section
    ==================================================
    */
    uint256 public startTime; //The start date of the initial auction
    uint256 public endTime; //The end date of the initial auction

    uint256 public hammerBlockDuration; //The minium number of blocks for which other bidder can come in after a winning offer
    uint256 public hammerTimeDuration; //The  number of seconds for which other bidder can come in after a winning offer
    uint256 public hammerBlock; //The block number after which a winning offer can claim a cryptograph
    uint256 public hammerTime; //The date after which a winning offer can claim a cryptograph

    /*
    ==================================================
                        Binding section
    ==================================================
    */
    address public auctionHouse; //The address of the auction house
    address public myCryptograph; //The address of the Cryptograph I'm administrating
    address public cryFactory; //The address of the cryptograph Factory

    bool public initialized;
    bool public isBeingERC2665Approved; //If set to true, a potential new owner has been approved in ERC2665
}


File 34 of 40: TheCryptographLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./TheCryptographV1.sol";

import "./CryptographFactoryV1.sol";
import "./AuctionHouseV1.sol";
import "./ERC2665LogicV1.sol";
import "./SingleAuctionLogicV1.sol";
import "./CryptographInitiator.sol";

/// @author Guillaume Gonnaud 2019
/// @title TheCryptograph Logic Code
/// @notice Represent a single Cryptograph. Contain provenance, ownership and renatus.
contract TheCryptographLogicV1 is VCProxyData, TheCryptographHeaderV1, TheCryptographStoragePublicV1 {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor()public{
        //Self intialize (nothing)
    }

    //Modifier for functions that requires to be called only by the Factory
    modifier restrictedToFactory() {
        require(SingleAuctionLogicV1(myAuction).cryFactory() == msg.sender, "Only callable by the factory");
        _;
    }

    /// @notice Init function of TheCryptograph
    /// @param _issue The issue # of this cryptograph
    /// @param _serial The serial # of this cryptograph (only relevant for editions and GGBMA)
    /// @param _official Is it an official or a community cryptograph ?
    /// @param _myAuction The address of our paired auction
    /// @param _cryInitiator The address of the initiator we are gonna grab name and media hash/url from
    /// @param _owner The initial owner. Always 0x0 except for GGBMA minted.
    function initCry(
        uint256 _issue, uint256 _serial, bool _official, address _myAuction, address _cryInitiator, address _owner) external {

        //Can only init if we are either (never init before) or if (we are official, before the auction starting time, and not under renatus)
        require(
            myAuction == address(0) ||
            (
                official &&
                !hasCurrentOwnerMarked &&
                SingleAuctionLogicV1(myAuction).cryFactory() == msg.sender &&
                SingleAuctionLogicV1(myAuction).startTime() > now),
            "This Cryptograph has already been initialized");
        //When renatus is happening, hasCurrentOwnerMarked should be set to true so that perpetual Altruism can't edit again the cryptograph

        //Setting up cryptograph identity related vars
        name = CryptographInitiator(_cryInitiator).name();
        creator = CryptographInitiator(_cryInitiator).creator();

        emit Named(name);

        mediaHash = CryptographInitiator(_cryInitiator).mediaHash();
        emit MediaHash(mediaHash);

        mediaUrl = CryptographInitiator(_cryInitiator).mediaUrl();
        emit MediaUrl(mediaUrl);

        serial = _serial;
        issue = _issue;

        official = _official;

        //Setting up initial owner (nobody EXCEPT GGBMA minting)
        owner = _owner;

        //Linking the auction
        myAuction = _myAuction;

    }

    /// @notice Set the media hash of the Cryptograph
    /// @dev Advanced requirement checks should be done on the factory side
    /// @param _mediaHash A string containing the media hash
    function setMediaHash(string calldata _mediaHash) external restrictedToFactory() {
        mediaHash = _mediaHash;
        emit MediaHash(_mediaHash);
    }

    /// @notice Set the media url of the Cryptograph
    /// @dev Advanced requirement checks should be done on the factory side
    /// @param _mediaUrl A string containing the media url
    function setMediaUrl(string calldata _mediaUrl)external restrictedToFactory() {
        mediaUrl = _mediaUrl;
        emit MediaUrl(_mediaUrl);
    }

    /// @notice Transfer ownership of the token
    /// @dev only callable by the associated GBM auction instance
    /// @param _newOwner The address of the account to become the new owner
    function transfer(address _newOwner) external {
        require(msg.sender == myAuction, "The auction is the only way to set a new owner");
        emit Transferred(owner, _newOwner);
        owner = _newOwner;
        hasCurrentOwnerMarked = false;

        //Resetting renatus timer
        lastOwnerInteraction = now;
        renatusTimeStamp = 0;
    }

    /// @notice Mark a cryptograph
    /// @dev only callable by the current owner if he has not done it since he gained ownership
    /// @param _mark A 3 Character long string containing the mark
    function mark(string calldata _mark) external {
        require(msg.sender == owner, "Only the owner can set a mark on a cryptograph");
        require(!hasCurrentOwnerMarked, "The cryptograph has already been marked by the owner");
        require(bytes(_mark).length <= 3, "You can only inscribe at most 3 characters at a time"); //In Utf8, strlenght <= bytelength.

        hasCurrentOwnerMarked = true; //Setting the current owner has having marked

        marks.push(_mark); //Inscribing the mark
        markers.push(owner); //Associating the owner

        emit Marked(owner, _mark); //Emitting the event

        //Resetting renatus timer
        lastOwnerInteraction = now;
        renatusTimeStamp = 0;
    }

    /// @notice Prevent burning cryptographs by putting them back to auctions if abandoned by their owners
    /// @dev If called by the owner, and ERC-2665 operator of perpetual altruism refresh ownership for 5 years
    function renatus() external {
        if (msg.sender == owner ||
            msg.sender == myAuction ||
            msg.sender == SingleAuctionLogicV1(myAuction).publisher() ||
            msg.sender == AuctionHouseStoragePublicV1(SingleAuctionLogicV1(myAuction).auctionHouse()).ERC2665Lieutenant()) {
            lastOwnerInteraction = now; //If the owner/operator/Pa call, reset the renatus call
            renatusTimeStamp = 0;
            emit Renatus(0);
        } else {
            require(now >= lastOwnerInteraction + 60 * 60 * 24 * 366 * 5, "Five years have not yet elapsed since last owner interaction");

            // Set up a 31 day deadline for the owner to claim their Cryptograph again
            if (renatusTimeStamp == 0) {
                renatusTimeStamp = now + 60 * 60 * 24 * 31;
                //Emit the event
                emit Renatus(renatusTimeStamp);
            } else {
                require(now > renatusTimeStamp, "31 days since renatus was called have not elapsed yet");

                SingleAuctionLogicV1(myAuction).renatus();

                //Notify the ERC2665 contract
                ERC2665LogicV1(AuctionHouseStoragePublicV1(SingleAuctionLogicV1(myAuction).auctionHouse()).ERC2665Lieutenant()).triggerRenatus();
                hasCurrentOwnerMarked = true; //Prevent publisher meddling
            }
        }
    }

}

File 35 of 40: TheCryptographProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./TheCryptographV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  TheCryptograph Proxy Smart Contract
/// @notice TheCryptograph proxy : this is this contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract TheCryptographProxiedV1 is VCProxy, TheCryptographHeaderV1, TheCryptographStorageInternalV1  {

    constructor(uint256 _version, address _vc)  public
    VCProxy(_version, _vc) //Call the VC proxy constructor so that we know where our logic code is
    {
        //Self intialize (nothing)
    }

    //No other logic code as it is all proxied

}





File 36 of 40: TheCryptographV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";

/// @author Guillaume Gonnaud 2019
/// @title TheCryptograph Header
/// @notice Contain all the events emitted by TheCryptograph
contract TheCryptographHeaderV1 {
    event Named(string name);
    event MediaHash(string mediaHash);
    event MediaUrl(string mediaUrl);
    event Transferred(address indexed previousOwner, address indexed newOwner);
    event Marked (address indexed Marker, string indexed Mark);
    event Renatus(uint256 endtime);
}


/// @author Guillaume Gonnaud 2019
/// @title TheCryptograph Storage Internal
/// @notice Contain all the storage of TheCryptograph declared in a way that don't generate getters for Proxy use
contract TheCryptographStorageInternalV1 {

    /*
    ==================================================
                    Identity Section
    ==================================================
    */
    string internal name; //The name of this cryptograph
    string internal creator; //The creator of this cryptograph
    string internal mediaHash; //The hash of the cryptograph media
    string internal mediaUrl; //An url where the cryptograph media is accessible
    uint256 internal serial; //The serial number of this cryptograph (position in the index)
    uint256 internal issue; //The numbered minting of this specific cryptograph.
    bool internal hasCurrentOwnerMarked; //Each subsequent owner can only leave its mark once
    string[] internal marks; //Each owner can leave its mark on the cryptograph
    address[] internal markers; //List of owners that have left a mark

    /*
    ==================================================
                        Ownership section
    ==================================================
    */
    address internal owner; //The current owner of the cryptograph

    /*
    ==================================================
                    Auction Section
    ==================================================
    */
    address internal myAuction; //Address of the running auction associated with this Cryptograph
    bool internal official; //Are we an official cryptograph ?

    /*
    ==================================================
                    Renatus Section
    ==================================================
    */
    uint256 internal lastOwnerInteraction; //When was the last time the owner interacted with the cryptograph ?
    uint256 internal renatusTimeStamp; //When was the last time someone wanted to check if the owner was still owning it's private key ?

}


/// @author Guillaume Gonnaud 2019
/// @title TheCryptograph Storage Public
/// @notice Contain all the storage of TheCryptograph declared in a way that generates getters for Logic use
contract TheCryptographStoragePublicV1 {

    /*
    ==================================================
                    Identity Section
    ==================================================
    */
    string public name; //The name of this cryptograph
    string public creator; //The creator of this cryptograph
    string public mediaHash; //The hash of the cryptograph media
    string public mediaUrl; //An url where the cryptograph media is accessible
    uint256 public serial; //The serial number of this cryptograph (position in the index)
    uint256 public issue;
    bool public hasCurrentOwnerMarked; //Each subsequent owner can only leave its mark once
    string[] public marks; //Each owner can leave its mark on the cryptograph
    address[] public markers; //List of owners that have left a mark

    /*
    ==================================================
                        Ownership Section
    ==================================================
    */
    address public owner; //The current owner of the cryptograph

    /*
    ==================================================
                    Auction Section
    ==================================================
    */
    address public myAuction; //Address of the running auction associated with this Cryptograph
    bool public official; //Are we an official cryptograph ?

    /*
    ==================================================
                    Renatus Section
    ==================================================
    */
    uint256 public lastOwnerInteraction; //When was the last time the owner interacted with the cryptograph ?
    uint256 public renatusTimeStamp; //When was the last time someone wanted to check if the owner was still owning it's private key ?

}


File 37 of 40: VCProxy.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

/* Based on a variation of https://blog.gnosis.pm/solidity-delegateproxy-contracts-e09957d0f201
This generic proxy is gonna ask a version control smart contract for its logic code instead
of storing the remote address himself
*/

/*
Smart contract only containing a public array named the same as VC so that the compiler call the proper
function signature in our generic proxy
*/
contract VersionControlStoragePublic {
    address[] public code;
}


/*
Storage stack of a proxy contract. VCproxy inherit this, as well as ALL logic contracts associated to a proxy for storage alignment reasons.
*/
contract VCProxyData {
    address internal vc; //Version Control Smart Contract Address
    uint256 internal version; //The index of our logic code in the Version Control array.
}


/*
Logic of a proxy contract. EVERY proxied contract inherit this
*/
contract VCProxy is VCProxyData {
    constructor(uint256 _version, address _vc) public {
        version = _version;
        vc = _vc;
    }

    fallback () virtual external payable {

        address addr = VersionControlStoragePublic(vc).code(version);
        assembly {
            let freememstart := mload(0x40)
            calldatacopy(freememstart, 0, calldatasize())
            let success := delegatecall(not(0), addr, freememstart, calldatasize(), freememstart, 0)
            returndatacopy(freememstart, 0, returndatasize())
            switch success
            case 0 { revert(freememstart, returndatasize()) }
            default { return(freememstart, returndatasize()) }
        }
    }

    
    /// @notice Generic catch-all function that refuse payments to prevent accidental Eth burn.
    receive() virtual external payable{
       require(false, "Do not send me Eth without a reason");
    }
}

File 38 of 40: VersionControlLogicV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./VersionControlV1.sol";
import "./SenateLogicV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title Logic code smart contract for proper versioning of proxies. Logic code, to be casted on the proxy.
contract VersionControlLogicV1 is VCProxyData, VersionControlHeaderV1, VersionControlStoragePublicV1  {

    /// @notice Generic constructor, empty
    /// @dev This contract is meant to be used in a delegatecall and hence its memory state is irrelevant
    constructor() public {
        //Memory state for logic smart contract is irrelevant
    }

    //Modifier for functions that requires to be called only by the controller of the version control
    modifier restrictedToController(){
        require(msg.sender == controller, "Only the controller can call this function");
        _;
    }

    /// @notice Set the code address of a specific version to the new specified code address
    /// @dev To be overhauled with voting in the future
    /// @param _version The version that is stored by the smart contract you want to change the logic code of
    /// @param _code The new code address
    function setVersion(uint256 _version, address _code) public restrictedToController(){ //Need to be restricted to PA only
        bool authorization = senate == address(0x0); //We check if the senate is set
        if(!authorization){ //If the senate is set, ask for authorization
            authorization = SenateLogicV1(senate).isAddressAllowed(_code);
        }
        require(authorization, "The senate -voting smart contract- did not allow this address to be used");
        emit VCChangedVersion(_version, code[_version], _code);
        code[_version] = _code;
    }

    /// @notice Push a new address in the versioning Ledger
    /// @dev Must be approved by the senate
    /// @param _code The new code address
    /// @return The number of Cryptographs owned by `_owner`, possibly zero
    function pushVersion(address _code) public restrictedToController() returns (uint256){ //Need to be restricted to PA only
        bool authorization = senate == address(0x0); //We check if the senate is set
        if(!authorization){ //If the senate is set, ask for authorization
            authorization = SenateLogicV1(senate).isAddressAllowed(_code);
        }
        require(authorization, "The senate -voting smart contract- did not allow this address to be pushed");
        code.push(_code);
        uint256 index = code.length - 1;
        emit VCCAddedVersion(index, _code);
        return index;
    }

    /// @notice Expose the length of the code array
    /// @dev Useful to know the index of the last inserted code element
    /// @return The lenght of the code array
    function codeLength() external view returns (uint256){
        return code.length;
    }

    /// @notice Push a new address in the versioning Ledger
    /// @dev Can be set up once only
    /// @param _senate The new code address
    function setSenate (address _senate) public restrictedToController(){
        require(senate == address(0x0), "The senate address has already been set");
        senate = _senate;
    }

}

File 39 of 40: VersionControlProxiedV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";
import "./VersionControlV1.sol";

/// @author Guillaume Gonnaud 2019
/// @title  VCProxy Proxy Smart Contract
/// @notice VCProxy proxy : this is the contract that will be instancied on the blockchain. Cast this as the logic contract to interact with it.
contract VersionControlProxiedV1 is VCProxy, VersionControlHeaderV1, VersionControlStorageInternalV1  {

    constructor(address _vc)  public
    VCProxy(0, _vc) //Call the VC proxy constructor with 0 as index paramter
    {
        //Self initialize
        controller = msg.sender;
        code.push(_vc); //Push the address of the VC logic code at index 0
        emit VCCAddedVersion(0, _vc); //Fire relevant push event
    }

    /*
    trick to avoid infinite loop when a Version Control proxy calls itself : we override the VCProxy fallback function and
    get the address from our own array instead of stacking one more call
    */

    fallback () external payable override{
        address addr = code[version];
        assembly{
            let freememstart := mload(0x40)
            calldatacopy(freememstart, 0, calldatasize())
            let success := delegatecall(not(0), addr, freememstart, calldatasize(), freememstart, 0)
            returndatacopy(freememstart, 0, returndatasize())
            switch success
            case 0 { revert(freememstart, returndatasize()) }
            default { return(freememstart, returndatasize()) }
        }
    }
   
}



File 40 of 40: VersionControlV1.sol
// © Copyright 2020. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity 0.6.6;

import "./VCProxy.sol";

/// @author Guillaume Gonnaud 2019
/// @title Version Control Header
/// @notice Contain all the events emitted by the Version Control
contract VersionControlHeaderV1 {
    event VCChangedVersion(uint256 index, address oldCode, address newCode);
    event VCCAddedVersion(uint256 index, address newCode);
}


/// @author Guillaume Gonnaud 2019
/// @title TheCryptograph Storage Internal
/// @notice Contain all the storage of TheCryptograph declared in a way that does not generate getters for Proxy use
contract VersionControlStorageInternalV1 {
    address[] public code; //Public to shortcut lookups to it in proxy calls
    address internal controller;
    address internal senate;
}


/// @author Guillaume Gonnaud 2019
/// @title TheCryptograph Storage Public
/// @notice Contain all the storage of TheCryptograph declared in a way that generates getters for Logic use
contract VersionControlStoragePublicV1 {
    address[] public code; //Public for ABI reasons, should be internal for strict gas saving
    address public controller;
    address public senate;
}


Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_version","type":"uint256"},{"internalType":"address","name":"_vc","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":false,"internalType":"bool","name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"approvedTransferAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b5060405161030c38038061030c8339818101604052604081101561003357600080fd5b810190808051906020019092919080519060200190929190505050818181600181905550806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050610262806100aa6000396000f3fe6080604052600436106100225760003560e01c8063c4c30f4c1461015b57610080565b3661008057600061007e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061020a6023913960400191505060405180910390fd5b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332e2fa426001546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156100f657600080fd5b505afa15801561010a573d6000803e3d6000fd5b505050506040513d602081101561012057600080fd5b810190808051906020019092919050505090506040513660008237600081368385600019f43d6000833e8060008114610157573d83f35b3d83fd5b34801561016757600080fd5b506101946004803603602081101561017e57600080fd5b81019080803590602001909291905050506101d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600d6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fe446f206e6f742073656e64206d652045746820776974686f7574206120726561736f6ea26469706673582212206c4c7c163456de6144736c238834046ac19d8dfcca7af1557fba0d6d8397e18864736f6c6343000606003300000000000000000000000000000000000000000000000000000000000000050000000000000000000000001a9700c73fc51571e5da48adf1c58e084084270f

Deployed Bytecode

0x6080604052600436106100225760003560e01c8063c4c30f4c1461015b57610080565b3661008057600061007e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061020a6023913960400191505060405180910390fd5b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332e2fa426001546040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b1580156100f657600080fd5b505afa15801561010a573d6000803e3d6000fd5b505050506040513d602081101561012057600080fd5b810190808051906020019092919050505090506040513660008237600081368385600019f43d6000833e8060008114610157573d83f35b3d83fd5b34801561016757600080fd5b506101946004803603602081101561017e57600080fd5b81019080803590602001909291905050506101d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600d6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fe446f206e6f742073656e64206d652045746820776974686f7574206120726561736f6ea26469706673582212206c4c7c163456de6144736c238834046ac19d8dfcca7af1557fba0d6d8397e18864736f6c63430006060033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000050000000000000000000000001a9700c73fc51571e5da48adf1c58e084084270f

-----Decoded View---------------
Arg [0] : _version (uint256): 5
Arg [1] : _vc (address): 0x1a9700c73FC51571E5dA48adF1C58e084084270F

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [1] : 0000000000000000000000001a9700c73fc51571e5da48adf1c58e084084270f


Deployed Bytecode Sourcemap

413:343:17:-:0;;;;;;;;;;;;;;;;;;;;;;;1874:5:36;1866:53;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;413:343:17;;1188:12:36;1231:2;;;;;;;;;;;1203:36;;;1240:7;;1203:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;1203:45:36;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1203:45:36;;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;1203:45:36;;;;;;;;;;;;;;;;1188:60;;1309:4;1303:11;1358:14;1355:1;1341:12;1328:45;1473:1;1459:12;1443:14;1429:12;1423:4;1419:1;1415:6;1402:73;1521:16;1518:1;1504:12;1489:49;1559:7;1585:1;1580:49;;;;1674:16;1660:12;1653:38;1580:49;1610:16;1596:12;1589:38;14094:58:18;;5:9:-1;2:2;;;27:1;24;17:12;2:2;14094:58:18;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;14094:58:18;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o

Swarm Source

ipfs://6c4c7c163456de6144736c238834046ac19d8dfcca7af1557fba0d6d8397e188

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

OVERVIEW

Cryptograph provides one-of-a-kind digital collectibles from icons and artists that raise money for charity, secured by blockchain technology.

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ 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.