ETH Price: $2,124.42 (+3.82%)

Transaction Decoder

Block:
6471856 at Oct-07-2018 06:22:25 PM +UTC
Transaction Fee:
0.001102205 ETH $2.34
Gas Used:
84,785 Gas / 13 Gwei

Emitted Events:

36 RarePackFour.PacksPurchased( id=16306, user=[Sender] 0xc6a7463a7ee700d035aff7bfd1ee198d680a4164, count=100 )

Account State Difference:

  Address   Before After State Difference Code
(xnpool)
474.832305757390384626 Eth474.833407962390384626 Eth0.001102205
0x0777F76D...fCd286919
(GU: Rare Pack Four)
0x91B9d283...Bd04fd689
(GU: Capped Vault)
3,607.949870999999999 Eth3,609.149870999999999 Eth1.2
0xC6A7463A...D680A4164
2.622261812288725168 Eth
Nonce: 60
1.421159607288725168 Eth
Nonce: 61
1.201102205

Execution Trace

ETH 1.2 RarePackFour.purchase( packCount=100, referrer=0x0000000000000000000000000000000000000000 )
  • ETH 1.2 CappedVault.CALL( )
    File 1 of 2: RarePackFour
    pragma solidity 0.4.24;
    
    contract Ownable {
    
        address public owner;
    
        constructor() public {
            owner = msg.sender;
        }
    
        function setOwner(address _owner) public onlyOwner {
            owner = _owner;
        }
    
        modifier onlyOwner {
            require(msg.sender == owner);
            _;
        }
    
    }
    
    contract Vault is Ownable { 
    
        function () public payable {
    
        }
    
        function getBalance() public view returns (uint) {
            return address(this).balance;
        }
    
        function withdraw(uint amount) public onlyOwner {
            require(address(this).balance >= amount);
            owner.transfer(amount);
        }
    
        function withdrawAll() public onlyOwner {
            withdraw(address(this).balance);
        }
    }
    
    
    contract CappedVault is Vault { 
    
        uint public limit;
        uint withdrawn = 0;
    
        constructor() public {
            limit = 33333 ether;
        }
    
        function () public payable {
            require(total() + msg.value <= limit);
        }
    
        function total() public view returns(uint) {
            return getBalance() + withdrawn;
        }
    
        function withdraw(uint amount) public onlyOwner {
            require(address(this).balance >= amount);
            owner.transfer(amount);
            withdrawn += amount;
        }
    
    }
    
    
    contract PreviousInterface {
    
        function ownerOf(uint id) public view returns (address);
    
        function getCard(uint id) public view returns (uint16, uint16);
    
        function totalSupply() public view returns (uint);
    
        function burnCount() public view returns (uint);
    
    }
    
    contract Pausable is Ownable {
        
        event Pause();
        event Unpause();
    
        bool public paused = false;
    
    
        /**
        * @dev Modifier to make a function callable only when the contract is not paused.
        */
        modifier whenNotPaused() {
            require(!paused);
            _;
        }
    
        /**
        * @dev Modifier to make a function callable only when the contract is paused.
        */
        modifier whenPaused() {
            require(paused);
            _;
        }
    
        /**
        * @dev called by the owner to pause, triggers stopped state
        */
        function pause() onlyOwner whenNotPaused public {
            paused = true;
            emit Pause();
        }
    
        /**
        * @dev called by the owner to unpause, returns to normal state
        */
        function unpause() onlyOwner whenPaused public {
            paused = false;
            emit Unpause();
        }
    }
    
    contract Governable {
    
        event Pause();
        event Unpause();
    
        address public governor;
        bool public paused = false;
    
        constructor() public {
            governor = msg.sender;
        }
    
        function setGovernor(address _gov) public onlyGovernor {
            governor = _gov;
        }
    
        modifier onlyGovernor {
            require(msg.sender == governor);
            _;
        }
    
        /**
        * @dev Modifier to make a function callable only when the contract is not paused.
        */
        modifier whenNotPaused() {
            require(!paused);
            _;
        }
    
        /**
        * @dev Modifier to make a function callable only when the contract is paused.
        */
        modifier whenPaused() {
            require(paused);
            _;
        }
    
        /**
        * @dev called by the owner to pause, triggers stopped state
        */
        function pause() onlyGovernor whenNotPaused public {
            paused = true;
            emit Pause();
        }
    
        /**
        * @dev called by the owner to unpause, returns to normal state
        */
        function unpause() onlyGovernor whenPaused public {
            paused = false;
            emit Unpause();
        }
    
    }
    
    contract CardBase is Governable {
    
    
        struct Card {
            uint16 proto;
            uint16 purity;
        }
    
        function getCard(uint id) public view returns (uint16 proto, uint16 purity) {
            Card memory card = cards[id];
            return (card.proto, card.purity);
        }
    
        function getShine(uint16 purity) public pure returns (uint8) {
            return uint8(purity / 1000);
        }
    
        Card[] public cards;
        
    }
    
    contract CardProto is CardBase {
    
        event NewProtoCard(
            uint16 id, uint8 season, uint8 god, 
            Rarity rarity, uint8 mana, uint8 attack, 
            uint8 health, uint8 cardType, uint8 tribe, bool packable
        );
    
        struct Limit {
            uint64 limit;
            bool exists;
        }
    
        // limits for mythic cards
        mapping(uint16 => Limit) public limits;
    
        // can only set limits once
        function setLimit(uint16 id, uint64 limit) public onlyGovernor {
            Limit memory l = limits[id];
            require(!l.exists);
            limits[id] = Limit({
                limit: limit,
                exists: true
            });
        }
    
        function getLimit(uint16 id) public view returns (uint64 limit, bool set) {
            Limit memory l = limits[id];
            return (l.limit, l.exists);
        }
    
        // could make these arrays to save gas
        // not really necessary - will be update a very limited no of times
        mapping(uint8 => bool) public seasonTradable;
        mapping(uint8 => bool) public seasonTradabilityLocked;
        uint8 public currentSeason;
    
        function makeTradable(uint8 season) public onlyGovernor {
            seasonTradable[season] = true;
        }
    
        function makeUntradable(uint8 season) public onlyGovernor {
            require(!seasonTradabilityLocked[season]);
            seasonTradable[season] = false;
        }
    
        function makePermanantlyTradable(uint8 season) public onlyGovernor {
            require(seasonTradable[season]);
            seasonTradabilityLocked[season] = true;
        }
    
        function isTradable(uint16 proto) public view returns (bool) {
            return seasonTradable[protos[proto].season];
        }
    
        function nextSeason() public onlyGovernor {
            //Seasons shouldn't go to 0 if there is more than the uint8 should hold, the governor should know this ¯\_(ツ)_/¯ -M
            require(currentSeason <= 255); 
    
            currentSeason++;
            mythic.length = 0;
            legendary.length = 0;
            epic.length = 0;
            rare.length = 0;
            common.length = 0;
        }
    
        enum Rarity {
            Common,
            Rare,
            Epic,
            Legendary, 
            Mythic
        }
    
        uint8 constant SPELL = 1;
        uint8 constant MINION = 2;
        uint8 constant WEAPON = 3;
        uint8 constant HERO = 4;
    
        struct ProtoCard {
            bool exists;
            uint8 god;
            uint8 season;
            uint8 cardType;
            Rarity rarity;
            uint8 mana;
            uint8 attack;
            uint8 health;
            uint8 tribe;
        }
    
        // there is a particular design decision driving this:
        // need to be able to iterate over mythics only for card generation
        // don't store 5 different arrays: have to use 2 ids
        // better to bear this cost (2 bytes per proto card)
        // rather than 1 byte per instance
    
        uint16 public protoCount;
        
        mapping(uint16 => ProtoCard) protos;
    
        uint16[] public mythic;
        uint16[] public legendary;
        uint16[] public epic;
        uint16[] public rare;
        uint16[] public common;
    
        function addProtos(
            uint16[] externalIDs, uint8[] gods, Rarity[] rarities, uint8[] manas, uint8[] attacks, 
            uint8[] healths, uint8[] cardTypes, uint8[] tribes, bool[] packable
        ) public onlyGovernor returns(uint16) {
    
            for (uint i = 0; i < externalIDs.length; i++) {
    
                ProtoCard memory card = ProtoCard({
                    exists: true,
                    god: gods[i],
                    season: currentSeason,
                    cardType: cardTypes[i],
                    rarity: rarities[i],
                    mana: manas[i],
                    attack: attacks[i],
                    health: healths[i],
                    tribe: tribes[i]
                });
    
                _addProto(externalIDs[i], card, packable[i]);
            }
            
        }
    
        function addProto(
            uint16 externalID, uint8 god, Rarity rarity, uint8 mana, uint8 attack, uint8 health, uint8 cardType, uint8 tribe, bool packable
        ) public onlyGovernor returns(uint16) {
            ProtoCard memory card = ProtoCard({
                exists: true,
                god: god,
                season: currentSeason,
                cardType: cardType,
                rarity: rarity,
                mana: mana,
                attack: attack,
                health: health,
                tribe: tribe
            });
    
            _addProto(externalID, card, packable);
        }
    
        function addWeapon(
            uint16 externalID, uint8 god, Rarity rarity, uint8 mana, uint8 attack, uint8 durability, bool packable
        ) public onlyGovernor returns(uint16) {
    
            ProtoCard memory card = ProtoCard({
                exists: true,
                god: god,
                season: currentSeason,
                cardType: WEAPON,
                rarity: rarity,
                mana: mana,
                attack: attack,
                health: durability,
                tribe: 0
            });
    
            _addProto(externalID, card, packable);
        }
    
        function addSpell(uint16 externalID, uint8 god, Rarity rarity, uint8 mana, bool packable) public onlyGovernor returns(uint16) {
    
            ProtoCard memory card = ProtoCard({
                exists: true,
                god: god,
                season: currentSeason,
                cardType: SPELL,
                rarity: rarity,
                mana: mana,
                attack: 0,
                health: 0,
                tribe: 0
            });
    
            _addProto(externalID, card, packable);
        }
    
        function addMinion(
            uint16 externalID, uint8 god, Rarity rarity, uint8 mana, uint8 attack, uint8 health, uint8 tribe, bool packable
        ) public onlyGovernor returns(uint16) {
    
            ProtoCard memory card = ProtoCard({
                exists: true,
                god: god,
                season: currentSeason,
                cardType: MINION,
                rarity: rarity,
                mana: mana,
                attack: attack,
                health: health,
                tribe: tribe
            });
    
            _addProto(externalID, card, packable);
        }
    
        function _addProto(uint16 externalID, ProtoCard memory card, bool packable) internal {
    
            require(!protos[externalID].exists);
    
            card.exists = true;
    
            protos[externalID] = card;
    
            protoCount++;
    
            emit NewProtoCard(
                externalID, currentSeason, card.god, 
                card.rarity, card.mana, card.attack, 
                card.health, card.cardType, card.tribe, packable
            );
    
            if (packable) {
                Rarity rarity = card.rarity;
                if (rarity == Rarity.Common) {
                    common.push(externalID);
                } else if (rarity == Rarity.Rare) {
                    rare.push(externalID);
                } else if (rarity == Rarity.Epic) {
                    epic.push(externalID);
                } else if (rarity == Rarity.Legendary) {
                    legendary.push(externalID);
                } else if (rarity == Rarity.Mythic) {
                    mythic.push(externalID);
                } else {
                    require(false);
                }
            }
        }
    
        function getProto(uint16 id) public view returns(
            bool exists, uint8 god, uint8 season, uint8 cardType, Rarity rarity, uint8 mana, uint8 attack, uint8 health, uint8 tribe
        ) {
            ProtoCard memory proto = protos[id];
            return (
                proto.exists,
                proto.god,
                proto.season,
                proto.cardType,
                proto.rarity,
                proto.mana,
                proto.attack,
                proto.health,
                proto.tribe
            );
        }
    
        function getRandomCard(Rarity rarity, uint16 random) public view returns (uint16) {
            // modulo bias is fine - creates rarity tiers etc
            // will obviously revert is there are no cards of that type: this is expected - should never happen
            if (rarity == Rarity.Common) {
                return common[random % common.length];
            } else if (rarity == Rarity.Rare) {
                return rare[random % rare.length];
            } else if (rarity == Rarity.Epic) {
                return epic[random % epic.length];
            } else if (rarity == Rarity.Legendary) {
                return legendary[random % legendary.length];
            } else if (rarity == Rarity.Mythic) {
                // make sure a mythic is available
                uint16 id;
                uint64 limit;
                bool set;
                for (uint i = 0; i < mythic.length; i++) {
                    id = mythic[(random + i) % mythic.length];
                    (limit, set) = getLimit(id);
                    if (set && limit > 0){
                        return id;
                    }
                }
                // if not, they get a legendary :(
                return legendary[random % legendary.length];
            }
            require(false);
            return 0;
        }
    
        // can never adjust tradable cards
        // each season gets a 'balancing beta'
        // totally immutable: season, rarity
        function replaceProto(
            uint16 index, uint8 god, uint8 cardType, uint8 mana, uint8 attack, uint8 health, uint8 tribe
        ) public onlyGovernor {
            ProtoCard memory pc = protos[index];
            require(!seasonTradable[pc.season]);
            protos[index] = ProtoCard({
                exists: true,
                god: god,
                season: pc.season,
                cardType: cardType,
                rarity: pc.rarity,
                mana: mana,
                attack: attack,
                health: health,
                tribe: tribe
            });
        }
    
    }
    
    contract MigrationInterface {
    
        function createCard(address user, uint16 proto, uint16 purity) public returns (uint);
    
        function getRandomCard(CardProto.Rarity rarity, uint16 random) public view returns (uint16);
    
        function migrate(uint id) public;
    
    }
    contract CardPackFour {
    
        MigrationInterface public migration;
        uint public creationBlock;
    
        constructor(MigrationInterface _core) public payable {
            migration = _core;
            creationBlock = 5939061 + 2000; // set to creation block of first contracts + 8 hours for down time
        }
    
        event Referral(address indexed referrer, uint value, address purchaser);
    
        /**
        * purchase 'count' of this type of pack
        */
        function purchase(uint16 packCount, address referrer) public payable;
    
        // store purity and shine as one number to save users gas
        function _getPurity(uint16 randOne, uint16 randTwo) internal pure returns (uint16) {
            if (randOne >= 998) {
                return 3000 + randTwo;
            } else if (randOne >= 988) {
                return 2000 + randTwo;
            } else if (randOne >= 938) {
                return 1000 + randTwo;
            } else {
                return randTwo;
            }
        }
    
    }
    
    contract FirstPheonix is Pausable {
    
        MigrationInterface core;
    
        constructor(MigrationInterface _core) public {
            core = _core;
        }
    
        address[] public approved;
    
        uint16 PHEONIX_PROTO = 380;
    
        mapping(address => bool) public claimed;
    
        function approvePack(address toApprove) public onlyOwner {
            approved.push(toApprove);
        }
    
        function isApproved(address test) public view returns (bool) {
            for (uint i = 0; i < approved.length; i++) {
                if (approved[i] == test) {
                    return true;
                }
            }
            return false;
        }
    
        // pause once cards become tradable
        function claimPheonix(address user) public returns (bool){
    
            require(isApproved(msg.sender));
    
            if (claimed[user] || paused){
                return false;
            }
    
            claimed[user] = true;
    
            core.createCard(user, PHEONIX_PROTO, 0);
    
            return true;
        }
    
    }
    
    contract PresalePackFour is CardPackFour, Pausable {
    
        CappedVault public vault;
    
        Purchase[] public purchases;
    
        function getPurchaseCount() public view returns (uint) {
            return purchases.length;
        }
    
        struct Purchase {
            uint16 current;
            uint16 count;
            address user;
            uint randomness;
            uint64 commit;
        }
    
        event PacksPurchased(uint indexed id, address indexed user, uint16 count);
        event PackOpened(uint indexed id, uint16 startIndex, address indexed user, uint[] cardIDs);
        event RandomnessReceived(uint indexed id, address indexed user, uint16 count, uint randomness);
        event Recommit(uint indexed id);
    
        constructor(MigrationInterface _core, CappedVault _vault) public payable CardPackFour(_core) {
            vault = _vault;
        }
    
        function basePrice() public returns (uint);
        function getCardDetails(uint16 packIndex, uint8 cardIndex, uint result) public view returns (uint16 proto, uint16 purity);
        
        function packSize() public view returns (uint8) {
            return 5;
        }
    
        uint16 public perClaim = 15;
    
        function setPacksPerClaim(uint16 _perClaim) public onlyOwner {
            perClaim = _perClaim;
        }
    
        function packsPerClaim() public view returns (uint16) {
            return perClaim;
        }
    
        // start in bytes, length in bytes
        function extract(uint num, uint length, uint start) internal pure returns (uint) {
            return (((1 << (length * 8)) - 1) & (num >> ((start * 8) - 1)));
        }
    
        function purchaseFor(address user, uint16 packCount, address referrer) whenNotPaused public payable {
            _purchase(user, packCount, referrer);
        }
    
        function purchase(uint16 packCount, address referrer) whenNotPaused public payable {
            _purchase(msg.sender, packCount, referrer);
        }
    
        function _purchase(address user, uint16 packCount, address referrer) internal {
            require(packCount > 0);
            require(referrer != user);
    
            uint price = calculatePrice(basePrice(), packCount);
    
            require(msg.value >= price);
    
            Purchase memory p = Purchase({
                user: user,
                count: packCount,
                commit: uint64(block.number),
                randomness: 0,
                current: 0
            });
    
            uint id = purchases.push(p) - 1;
    
            emit PacksPurchased(id, user, packCount);
    
            if (referrer != address(0)) {
                uint commission = price / 10;
                referrer.transfer(commission);
                price -= commission;
                emit Referral(referrer, commission, user);
            }
            
            address(vault).transfer(price);
        }
    
        // can recommit
        // this gives you more chances
        // if no-one else sends the callback (should never happen)
        // still only get a random extra chance
        function recommit(uint id) public {
    
            Purchase storage p = purchases[id];
    
            require(p.randomness == 0);
    
            require(block.number >= p.commit + 256);
    
            p.commit = uint64(block.number);
    
            emit Recommit(id);
        }
    
        // can be called by anybody
        // can miners withhold blocks --> not really
        // giving up block reward for extra chance --> still really low
        function callback(uint id) public {
    
            Purchase storage p = purchases[id];
    
            require(p.randomness == 0);
    
            // must be within last 256 blocks, otherwise recommit
            require(block.number - 256 < p.commit);
    
            // can't callback on the original block
            require(uint64(block.number) != p.commit);
    
            bytes32 bhash = blockhash(p.commit);
            // will get the same on every block
            // only use properties which can't be altered by the user
            uint random = uint(keccak256(abi.encodePacked(bhash, p.user, address(this), p.count)));
    
            require(uint(bhash) != 0);
    
            p.randomness = random;
    
            emit RandomnessReceived(id, p.user, p.count, p.randomness);
        }
    
        function claim(uint id) public {
            
            Purchase storage p = purchases[id];
    
            require(canClaim);
    
            uint16 proto;
            uint16 purity;
            uint16 count = p.count;
            uint result = p.randomness;
            uint8 size = packSize();
    
            address user = p.user;
            uint16 current = p.current;
    
            require(result != 0); // have to wait for the callback
            // require(user == msg.sender); // not needed
            require(count > 0);
    
            uint[] memory ids = new uint[](size);
    
            uint16 end = current + packsPerClaim() > count ? count : current + packsPerClaim();
    
            require(end > current);
    
            for (uint16 i = current; i < end; i++) {
                for (uint8 j = 0; j < size; j++) {
                    (proto, purity) = getCardDetails(i, j, result);
                    ids[j] = migration.createCard(user, proto, purity);
                }
                emit PackOpened(id, (i * size), user, ids);
            }
            p.current += (end - current);
        }
    
        function predictPacks(uint id) external view returns (uint16[] protos, uint16[] purities) {
    
            Purchase memory p = purchases[id];
    
            uint16 proto;
            uint16 purity;
            uint16 count = p.count;
            uint result = p.randomness;
            uint8 size = packSize();
    
            purities = new uint16[](size * count);
            protos = new uint16[](size * count);
    
            for (uint16 i = 0; i < count; i++) {
                for (uint8 j = 0; j < size; j++) {
                    (proto, purity) = getCardDetails(i, j, result);
                    purities[(i * size) + j] = purity;
                    protos[(i * size) + j] = proto;
                }
            }
            return (protos, purities);
        }
    
        function calculatePrice(uint base, uint16 packCount) public view returns (uint) {
            // roughly 6k blocks per day
            uint difference = block.number - creationBlock;
            uint numDays = difference / 6000;
            if (20 > numDays) {
                return (base - (((20 - numDays) * base) / 100)) * packCount;
            }
            return base * packCount;
        }
    
        function _getCommonPlusRarity(uint32 rand) internal pure returns (CardProto.Rarity) {
            if (rand == 999999) {
                return CardProto.Rarity.Mythic;
            } else if (rand >= 998345) {
                return CardProto.Rarity.Legendary;
            } else if (rand >= 986765) {
                return CardProto.Rarity.Epic;
            } else if (rand >= 924890) {
                return CardProto.Rarity.Rare;
            } else {
                return CardProto.Rarity.Common;
            }
        }
    
        function _getRarePlusRarity(uint32 rand) internal pure returns (CardProto.Rarity) {
            if (rand == 999999) {
                return CardProto.Rarity.Mythic;
            } else if (rand >= 981615) {
                return CardProto.Rarity.Legendary;
            } else if (rand >= 852940) {
                return CardProto.Rarity.Epic;
            } else {
                return CardProto.Rarity.Rare;
            } 
        }
    
        function _getEpicPlusRarity(uint32 rand) internal pure returns (CardProto.Rarity) {
            if (rand == 999999) {
                return CardProto.Rarity.Mythic;
            } else if (rand >= 981615) {
                return CardProto.Rarity.Legendary;
            } else {
                return CardProto.Rarity.Epic;
            }
        }
    
        function _getLegendaryPlusRarity(uint32 rand) internal pure returns (CardProto.Rarity) {
            if (rand == 999999) {
                return CardProto.Rarity.Mythic;
            } else {
                return CardProto.Rarity.Legendary;
            } 
        }
    
        bool public canClaim = true;
    
        function setCanClaim(bool claim) public onlyOwner {
            canClaim = claim;
        }
    
        function getComponents(
            uint16 i, uint8 j, uint rand
        ) internal returns (
            uint random, uint32 rarityRandom, uint16 purityOne, uint16 purityTwo, uint16 protoRandom
        ) {
            random = uint(keccak256(abi.encodePacked(i, rand, j)));
            rarityRandom = uint32(extract(random, 4, 10) % 1000000);
            purityOne = uint16(extract(random, 2, 4) % 1000);
            purityTwo = uint16(extract(random, 2, 6) % 1000);
            protoRandom = uint16(extract(random, 2, 8) % (2**16-1));
            return (random, rarityRandom, purityOne, purityTwo, protoRandom);
        }
    
        function withdraw() public onlyOwner {
            owner.transfer(address(this).balance);
        }
    
    }
    
    contract PackFourMultiplier is PresalePackFour {
    
        address[] public packs;
        uint16 public multiplier = 3;
        FirstPheonix pheonix;
        PreviousInterface old;
    
        uint16 public packLimit = 5;
    
        constructor(PreviousInterface _old, address[] _packs, MigrationInterface _core, CappedVault vault, FirstPheonix _pheonix) 
            public PresalePackFour(_core, vault) 
        {
            packs = _packs;
            pheonix = _pheonix;
            old = _old;
        }
    
        function getCardCount() internal view returns (uint) {
            return old.totalSupply() + old.burnCount();
        }
    
        function isPriorPack(address test) public view returns(bool) {
            for (uint i = 0; i < packs.length; i++) {
                if (packs[i] == test) {
                    return true;
                }
            }
            return false;
        }
    
        event Status(uint before, uint aft);
    
        function claimMultiple(address pack, uint purchaseID) public returns (uint16, address) {
    
            require(isPriorPack(pack));
    
            uint length = getCardCount();
    
            PresalePackFour(pack).claim(purchaseID);
    
            uint lengthAfter = getCardCount();
    
            require(lengthAfter > length);
    
            uint16 cardDifference = uint16(lengthAfter - length);
    
            require(cardDifference % 5 == 0);
    
            uint16 packCount = cardDifference / 5;
    
            uint16 extra = packCount * multiplier;
    
            address lastCardOwner = old.ownerOf(lengthAfter - 1);
    
            Purchase memory p = Purchase({
                user: lastCardOwner,
                count: extra,
                commit: uint64(block.number),
                randomness: 0,
                current: 0
            });
    
            uint id = purchases.push(p) - 1;
    
            emit PacksPurchased(id, lastCardOwner, extra);
    
            // try to give them a first pheonix
            pheonix.claimPheonix(lastCardOwner);
    
            emit Status(length, lengthAfter);
    
    
            if (packCount <= packLimit) {
                for (uint i = 0; i < cardDifference; i++) {
                    migration.migrate(lengthAfter - 1 - i);
                }
            }
    
            return (extra, lastCardOwner);
        }
    
        function setPackLimit(uint16 limit) public onlyOwner {
            packLimit = limit;
        }
    
    
    }
    
    contract RarePackFour is PackFourMultiplier {
        
        function basePrice() public returns (uint) {
            return 12 finney;
        }
    
        constructor(PreviousInterface _old, address[] _packs, MigrationInterface _core, CappedVault vault, FirstPheonix _pheonix) 
            public PackFourMultiplier(_old, _packs, _core, vault, _pheonix) {
            
        }
    
        function getCardDetails(uint16 packIndex, uint8 cardIndex, uint result) public view returns (uint16 proto, uint16 purity) {
            uint random;
            uint32 rarityRandom;
            uint16 protoRandom;
            uint16 purityOne;
            uint16 purityTwo;
            CardProto.Rarity rarity;
    
            (random, rarityRandom, purityOne, purityTwo, protoRandom) = getComponents(packIndex, cardIndex, result);
    
            if (cardIndex == 4) {
                rarity = _getRarePlusRarity(rarityRandom);
            } else {
                rarity = _getCommonPlusRarity(rarityRandom);
            }
    
            purity = _getPurity(purityOne, purityTwo);
        
            proto = migration.getRandomCard(rarity, protoRandom);
            return (proto, purity);
        }  
        
    }

    File 2 of 2: CappedVault
    pragma solidity 0.4.24;
    
    contract Ownable {
    
       address public owner;
    
       constructor() public {
           owner = msg.sender;
       }
    
       function setOwner(address _owner) public onlyOwner {
           owner = _owner;
       }
    
       modifier onlyOwner {
           require(msg.sender == owner);
           _;
       }
    
    }
    
    contract Vault is Ownable {
    
       function () public payable {
    
       }
    
       function getBalance() public view returns (uint) {
           return address(this).balance;
       }
    
       function withdraw(uint amount) public onlyOwner {
           require(address(this).balance >= amount);
           owner.transfer(amount);
       }
    
       function withdrawAll() public onlyOwner {
           withdraw(address(this).balance);
       }
    }
    
    contract CappedVault is Vault { 
    
        uint public limit;
        uint withdrawn = 0;
    
        constructor() public {
            limit = 33333 ether;
        }
    
        function () public payable {
            require(total() + msg.value <= limit);
        }
    
        function total() public view returns(uint) {
            return getBalance() + withdrawn;
        }
    
        function withdraw(uint amount) public onlyOwner {
            require(address(this).balance >= amount);
            owner.transfer(amount);
            withdrawn += amount;
        }
    
    }