ETH Price: $2,260.06 (-0.50%)
Gas: 0.67 Gwei

Transaction Decoder

Block:
8832786 at Oct-29-2019 07:37:39 AM +UTC
Transaction Fee:
0.000645337365616365 ETH $1.46
Gas Used:
118,065 Gas / 5.465949821 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
0x0777F76D...fCd286919
(GU: Rare Pack Four)
0x16c11dbB...e0f9bE8B3 5.03794 Eth5.03806 Eth0.00012
0x91B9d283...Bd04fd689
(GU: Capped Vault)
5,682.004170999999999 Eth5,682.014970999999999 Eth0.0108
43,826.749436167079614832 Eth43,826.750081504445231197 Eth0.000645337365616365
0xf784CD41...a9ea338fD
0.990795689820238617 Eth
Nonce: 80
0.979230352454622252 Eth
Nonce: 81
0.011565337365616365

Execution Trace

ETH 0.012 0x16c11dbba92aa59a0b67217841cf423e0f9be8b3.daaa1e5a( )
  • RarePackFour.STATICCALL( )
  • RarePackFour.calculatePrice( base=12000000000000000, packCount=1 ) => ( 12000000000000000 )
  • ETH 0.012 RarePackFour.purchaseFor( user=0xf784CD41Fd0Cd48133EdFB7D468f214a9ea338fD, packCount=1, referrer=0x16c11dbBA92Aa59A0B67217841cF423e0f9bE8B3 )
    • ETH 0.0012 0x16c11dbba92aa59a0b67217841cf423e0f9be8b3.CALL( )
    • ETH 0.0108 CappedVault.CALL( )
    • ETH 0.00108 0xf784cd41fd0cd48133edfb7d468f214a9ea338fd.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;
          }
      
      }