Full Smart Contract Upgradeability Discussion

Hi @Michal_Kosior

The function initialize is triggered when the contract is deployed.
https://github.com/filipmartinsson/Smart-Contract-Security/blob/master/Upgradeable-Advanced/contracts/DogsUpdated.sol

 constructor() public {
    initialize(msg.sender);
  }

  function initialize(address _owner) public {
    require(!_initialized);
    owner = _owner;
    _initialized = true;
  }

If I misunderstood your question let me know.

Cheers,
Dani

When contract is deployed ofc its ok. But we also triggered initialize in migration file:

  //Fool truffle once again. It now thinks proxyDog has all functions.
  proxyDog = await DogsUpdated.at(proxy.address);
  //Initialize proxy state.
  proxyDog.initialize(accounts[0]);

it’s also working because here we use state of proxy contract, but when we try to do another update e.g.

  proxyDog = await DogsUpdated2.at(proxy.address);
  proxyDog.initialize(accounts[0]);

we will have variable _initialized already set to true, isn’t it?

1 Like

Hi @Michal_Kosior

From your proxy contract you have to trigger initialise, keep in mind the ‘context’ of where the constructor is executed.
You are using a proxy.
Highly suggest this lecture: https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#the-constructor-caveat

Let me know,
Dani

1 Like

After learning about the proxy contract scheme, I dived a little deeper and checked out Open Zeppelin’s publications about this. I though it might be of interest to everyone so here is the link to Open Zeppelin’S proxy patterns explanation!

3 Likes

Hi There,

I’m trying to compile the 3 contracts and get the following error:

Compiling your contracts...
===========================
TypeError: Cannot read property 'imports' of undefined
    at Object.<anonymous> (C:\Users\Mariano Collarte\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\compile-common\dist\src\profiler\requiredSources.js:98:1)

I’ve checked the compiler version as well as the truffle.config file.

Any ideas?

Thanks!

Hi @Mariano_Collarte

Follow this faq and retry: FAQ - How to downgrade Node.Js

Cheers,
Dani

1 Like

Hi There,

Quick question.
Given the _initialized variable is tracked in the proxy contract and not the functional one, shouldn’t we also add to the upgrade function the reset of the _initialized flag like this?

function upgrade(address _newAddress) public onlyOwner{
		currentAddress = _newAddress;
		_initialized = false;
}

Or perhaps even in the constructor of the upgraded functional contract?

Thanks!

1 Like

I think I just realized that it wouldn’t be necessary because now I understand that the initialize function set’s the owner variable in the proxy contract.

1 Like

Hi There,

I ran into this issue again and found out that I believe this bug was reported and recently fixed in truffle version 5.3.3
I installed it and I’m no longer getting the error.
Just wanted to share:
https://github.com/trufflesuite/truffle/issues/3798

Thanks!

1 Like

Hello everybody, I need to ask something:
At 6:11(Part 8 Video) Filip says that he is calling the setNumberOfDogs(30) funtion in the DogsUpgraded.sol (See line 27)

imagen

But the contract that is still deployed in the proxy.address is the Dogs.sol, not the DogsUpgraded.sol.

He may have upgraded the address so that proxy.sol points now to DogsUpgraded.sol, BUT truffel is still fooled to see the Dogs.sol and not the DogsUpgraded.sol.

Makes sense what I am saying or may I be totally wrong?
Would really appreciate if someone elaborates this.
Thanks

1 Like

Hi @filip

Please see the code Below for Dogs.sol :slight_smile:

pragma solidity 0.5.12;
import "./Storage.sol";

contract Dogs is Storage{

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


  modifier onlyOwner() {
    require(msg.sender ==owner);
    _;
  }

  function getNumberOfDogs() public view returns(uint256) {
    return _uint256Storage["Dogs"];
  }

  function setNumberOfDogs(uint256 toSet) public {
    _uint256Storage["Dogs"] = toSet;
  }


}

And as you instructed in the Bonus Video, the below code for DogsUpdated looks easy and returns same results on Truffle (which is great because we just need to import Dogs in the DogsUpdated.sol file and change(if any functions need to be changed) and doesn’t affect our state/ scope change because storage of variables is anyways local to the Proxy contract interacting directly with other contracts/ EOA’s on the Blockchain)

pragma solidity 0.5.12;
import "./Dogs.sol";

contract DogsUpdated is Dogs {

  constructor() public {
    init(msg.sender);
  }

  function init(address _owner) public {
    require(!_initialized);
    owner = _owner;
    _initialized = true;
  }



}


But when I see the code above, it implies that anyone can copy my code and put it on the Blockchain just by adding an UpdatedDogs Contract instance since it is public and can be initialised by anyone. — This is what Open source really means ? The only thing separating a Good project from a bad Project is the domain name of its Website + Branding + Product (i.e. code strength), Right ? And Most important is the Domain Name of the webpage like lets say Aave.com, because thats what people who have used successfully will guide other people to use too thereby creating a network effect ?

That means that the difference between Ivan on tech academy or others (that may just copy/ paste code is that Ivan On tech has a great Partner/ Content Creator/ Educator like Filip while others may not have it and plus the great Branding, Have i understood it correctly from First Principles?

Like UniV2 which has been copied by several others on BSC (like PancakeSwap) ?

But even in such cases aren’t all these projects heavily dependent on AWS ?, and is there being anything done about this ?

Please help me figure this out from first principles, Otherwise I must say the Course + content was great just like all the other courses. and Your style of explaining deeply is immaculate. Thanks and Keep it up !!

Regards

Suveett kalra
(Su.Kal Crypto)

Hi @thecil @dan-i @Bhujanga

I have completed Dapp Programming and also Smart Contract Security and I am now planning to proceed to Chain Link 101 and DeFi 201.

in the meanwhile, i tried to convert my Dapp Introduction’s Kittycontract.sol and KittyMarketPlace.sol into a single Kittycontract.sol upgradeable contract together with proxy.sol, , storage.sol and UpdatedKittycontract.sol (as was shown in Security Course with Dogs.sol etc…, and Voila I was able to compile perfectly well in Truffle, so I am, happy with my Progress)

But I don’t know how to use openZeppellin and deploy this code on test Networks and thats where i need your Help. I tried studying the Docs online but couldn’t really figure out Much from That?

Can you share with me a step by step guide on how to do this or a Course in the academy on “How to deploy upgradeable Contracts” ?
Below is my Code :point_down:
Storage.sol :point_down:

pragma solidity 0.5.12;

contract Storage {
    address public contractOwner;

    //Mappings
    mapping (string => uint256) _uint256Storage;
    mapping(string => uint64) _uint64Storage;
    mapping (string => address) _addressStorage;
    mapping (string => bool) _boolStorage;
    mapping (string => bytes4) _bytesStorage;
    mapping (string => string) _stringStorage;

    //Struct with Cat Features:
    struct Cat {
        uint256 genes;
        uint64 birthTime;
        uint32 mumId;
        uint32 dadId;
        uint16 generation;
        address owner;
    }


    //Struct with Offer Details of the MarketPlace web app:
      struct Offer {
        address payable seller;
        uint256 price;
        uint256 index;
        uint256 tokenId;
        bool active;
      }


    //Mappings used in Kittycontract.sol:
    // TOKEN OWNERSHIP COUNT (OF OWNER) MAPPING:
    mapping(address => uint256) public tokenOwnershipCount;
    // OWNERSHIP CERTIFICATE(OF TOKEN ID) MAPPING:
    mapping(uint256 => address) public ownershipCertificate;
    // APPROVAL MAPPINGS
    mapping(uint256 => address) internal kittyIndexToApprove;
    // OPERATOR APPROVAL MAPPINGS:
    mapping(address => mapping(address => bool)) internal _operatorApprovals;

    //Mappings used in KittyMarketPlace.sol;
    //Mapping tokenId =>  struct
    mapping (uint256 => Offer) internal tokenIdToOffer;


    //State variables
    bool public _initialized;
    //address public _addressStorage["catOwner"];
    //address public _addressStorage["approvedAddress"]
    //uint256 public _uint256Storage["catTokenId"];
    //uint256 public _uint256Storage["kittyIndex"];
    //uint256 public _uint256Storage["numberOfCats"];
    //address public _addressStorage["ownerAddress"];
    //address public _addressStorage["operatorAddress"];
    string public constant Name = "MEOW-CATS";
    string public constant Symbol = "MEOW";
    uint256 internal constant CREATION_LIMIT_GEN0 = 100;
    uint64 internal  gen0Counter = 0;
    bytes4 internal constant MAGIC_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
    bytes4 internal constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
    bytes4 internal constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    //Cat array of Cat features- Used in Kittycontract.sol;
    Cat[] public cats;
    //Offer array of available Offers - Used in MarketPlace.sol;
    Offer[] offers;

}

Below is the Kittycontract.sol code :point_down:

pragma solidity 0.5.12;
import "./IERC721.sol";
import "./utils/SafeMath.sol";
import "./IERC721Receiver.sol";
import "./Storage.sol";
import "./IKittyMarketPlace.sol";



contract Kittycontract is IERC721, IKittyMarketPlace, Storage {

using SafeMath for uint256;

//##################################################################################################

// constructor
constructor() public {
    contractOwner = msg.sender;
    _createKitty(0,0, uint256(-1), 0, address(0));

}

modifier onlyOwner() {
    require(msg.sender == contractOwner);
    _; //Continue execution
}

//##################################################################################################
//Birth event  :
event Birth(
  address owner,
  uint256 genes,
  uint256 newKittenId,
  uint256 mumId,
  uint256 dadId);
//Transfer event (Whenever a Kitty is transferred from OldOwner to newOwner)
event Transfer(address indexed oldOwner, address indexed newOwner, uint256 indexed tokenId);
// Approval event (whenever the owner approves a third party address on its behalf)
event Approval(address indexed Owner, address indexed approvedTo, uint256 indexed tokenId);
// ApproveAll event( whenever the owner approves the Operator address)
event ApproveAll(address indexed Owner, address operator, bool success);
//MarketPlace event (Buying, newOffer, advertiseOffer, removerOffer etc..)
event MarketTransaction(string TxType, address owner, uint256 tokenId);
//#############################################################################################



function getContractOwner() public view returns (address) {
    return contractOwner;
}

//##############################################################################################################
//FUNCTIONS START HERE :
//Function when called from external contract returns whether this contract support IERC721 or IERC165 standard?
function supportsInterface(bytes4 _interfaceId) external view returns(bool) {
  return ( _interfaceId == _INTERFACE_ID_ERC165 || _interfaceId == _INTERFACE_ID_ERC721);
}

// SAFE TRANSFER FUNCTIONS
//Function executing an External Call from another User/ Contract(without Data):
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public {
  safeTransferFrom(_from, _to, _tokenId, " ");
}

//Function executing an Public Call which then goes through external Interface(called with @param bytes calldata _data):
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory _data) public {
  require(isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
  _safeTransfer(_from, _to, _tokenId, _data);
}


//Internal safeTransfer Function
function _safeTransfer(address _from, address _to, uint256 _tokenId, bytes memory _data) internal {
  require( _checkERC721Support(_from, _to, _tokenId, _data) );
  _transfer(_from, _to, _tokenId);
}

//Function executing an External Call from another User/ Contract:
function transferFrom(address _from, address _to, uint256 _tokenId) public {
  require(isApprovedOrOwner(msg.sender, _from, _to, _tokenId));
  //require(_to != address(0));
  //require(_to != address(this));
  //require(_owns(_from, _tokenId));
  //require(msg.sender == _from || approvedFor(msg.sender, _tokenId) || isApprovedForAll(_from, msg.sender));
  //require(_tokenId < cats.length);
  _transfer(_from, _to, _tokenId);

}

//Function executing an External Call from another User/ Contract:
function transfer(address _to, uint256 _tokenId) public {
  require(_to != address(0));
  require(_to != address(this));
  require(_owns(msg.sender, _tokenId));

  _transfer(msg.sender, _to, _tokenId);
}

//Actual transfer of the token happens from this below Internal Function:
function _transfer(address _from, address _to, uint256 _tokenId) internal {
  tokenOwnershipCount[_to]++ ;
  if(_from != address(0)){
    tokenOwnershipCount[_from]-- ;
    delete(kittyIndexToApprove[_tokenId]);//To disallow the Approved since Token is already sold at this stage:
  }

  //Event Transfer to be emitted now :
  emit Transfer(_from, _to, _tokenId);
}


//##########################################################################################################



//BASIC CAT CREATION, BREEDING & GETTER FUNCTIONS
function createKittyGen0(uint256 _genes) public onlyOwner returns(uint256){
  require(gen0Counter < CREATION_LIMIT_GEN0);
    gen0Counter ++;

  return _createKitty(0,0, _genes, 0, msg.sender);

}

function breed(uint256 _dadId, uint256 _mumId) public returns(uint256) {
  ( uint256 _dadDna,,,,, uint256 dadGeneration) = getKitty(_dadId);
  ( uint256 _mumDna,,,,, uint256 mumGeneration) = getKitty(_mumId);

  uint256 newDna = _mixDna(_dadDna, _mumDna);

  uint256 kidGen = 0;
  if (dadGeneration < mumGeneration) {
    kidGen = mumGeneration +1;
    kidGen /= 2;
  }
  else if (dadGeneration < mumGeneration) {
    kidGen = dadGeneration +1 ;
    kidGen /= 2;
  }
  else {
    kidGen = mumGeneration + 1;
  }
  _createKitty(_mumId, _dadId, newDna, kidGen, msg.sender);

  // Create the new kitty (with breeder becoming new kitties owner)
  uint256 newKittyId = _createKitty(
      _mumId,
      _dadId,
      kidGen,
      newDna,
      msg.sender
  );
  return newKittyId;
}

function _mixDna(uint256 _dadDna, uint256 _mumDna) internal returns(uint256) {
  //uint256 firstHalf = _dadDna /100000000;
  //uint256 secondhalf = _mumDna % 100000000;
  //uint256 newDna = firstHalf * 100000000;
  //newDna += secondhalf;
  //emit event DNA
  //emit NewDnaCreated(newDna);
  //return newDna;
  uint256[8] memory geneArray;
  uint256 i = 1;// lets say Random number is 11001011
  uint256 index = 7;
  uint8 random = uint8(now % 255);// binary between 00000000-11111111;
  for (i = 1; i <= 128; i = i*2 ){
    if(random & i != 0){
      geneArray[index] = uint8(_mumDna % 100);
    }
    else {
      geneArray[index] = uint8(_dadDna % 100);
    }
    _mumDna = _mumDna / 100;
    _dadDna = _dadDna / 100;
    index = index -1 ;
    //1,2,4,8,16,32,64, 128
    //00000001 - 1
    //00000010 - 2
    //00000100 -  4
    //00001000 - 8
    //00010000 - 16
    //00100000 - 32
    //10000000 - 64
    //10000000 - 128;
    /* if (true && false)
    11001011
    &
    00000001 = 1 // Keep checking all numbers against 11001011
    if(1) then mum _genes
    if(0) then dad _genes */
  }
  uint256 newGene;
  // [ 11, 22, 33, 44,55 ,66, 77, 88]
  for (i = 0; i < 8; i ++) {
    newGene = newGene + geneArray[i];
    if(i != 7) {
      newGene = newGene * 100;
    }
  }
  return newGene;
}


function _createKitty (
  uint256 _mumId,
  uint256 _dadId,
  uint256 _genes,
  uint256 _generation,
  address _owner
) private returns(uint256) {

  Cat memory _cat = Cat({
    genes : _genes,
    birthTime : uint64(now),
    mumId : uint32(_mumId),
    dadId : uint32(_dadId),
    generation : uint16(_generation),
    owner : _owner
  });
  uint256 newKittenId = cats.push(_cat) - 1;

  //Event Birth to  be emitted :
  emit Birth(_owner, _genes, newKittenId, _mumId, _dadId);

  _transfer(address(0), _owner, newKittenId);
  return newKittenId;
}


function getKitty(uint256 _newKittenId) public view returns(
uint256 _genes,
uint256 _mumId,
uint256 _dadId,
uint256 _birthTime,
address _owner,
uint256 _generation){

  Cat storage cat = cats[_newKittenId];
  _genes = cat.genes;
  _dadId = uint256(cat.dadId);
  _mumId = uint256(cat.mumId);
  _generation = uint256(cat.generation);
  _birthTime = uint256(cat.birthTime);
  _owner = cat.owner;

}

function getKittyByOwner(address _owner) external view returns(uint[] memory) {
  uint[] memory result = new uint[](tokenOwnershipCount[_owner]);
  uint counter = 0;
  for (uint i = 0; i < cats.length; i ++) {
    if (ownershipCertificate[i] == _owner) {
      result[counter] = i;
      counter ++;
    }
  }
  return result;
}

function balanceOf(address owner) external view returns (uint256 balance) {
  return tokenOwnershipCount[owner];
}

function totalSupply() public view returns (uint256 total) {
  total = cats.length;
  return total;
}

function name() external view returns (string memory tokenName) {
  tokenName = Name;
  return tokenName;
}


function symbol() external view returns (string memory tokenSymbol) {
  tokenSymbol = Symbol;
  return tokenSymbol;
}

function ownerOf(uint256 tokenId) public view returns (address owner) {
  return ownershipCertificate[tokenId];
}



//This below Function called "_owns" gives bool result whether the address has actual ownership of the _tokenId:
function _owns(address _claimant, uint256 _tokenId) internal view returns(bool) {
  return ownershipCertificate[_tokenId] == _claimant;
}



// APPROVE FUNCTION & GETTER FUNCTIONS RELATED TO APPROVALS :

function approve(address _to, uint256 _tokenId) public {
  require(_owns(msg.sender, _tokenId));
  _approve(_to, _tokenId);
  //emit event Approve
  emit Approval(msg.sender, _to, _tokenId);
}

function _approve(address _approved, uint _tokenId ) internal {
  _approved = kittyIndexToApprove[_tokenId];
}

function getApproved(uint256 _tokenId) public view returns (address) {
  require(_tokenId < cats.length); //Token must exist
  return kittyIndexToApprove[_tokenId];
}

function setApprovalForAll(address _operator, bool approved) public {
  require(_operator != msg.sender);
  approved = _operatorApprovals[msg.sender][_operator];
  //emit event ApproveAll
  emit ApproveAll(msg.sender, _operator, approved);
}

function approvedFor(address _claimant, uint256 _tokenId) internal view returns(bool) {
  return kittyIndexToApprove[_tokenId] == _claimant;
}


function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
  return _operatorApprovals[_owner][_operator];
}

function isApprovedOrOwner(address _spender, address _from, address _to, uint256 _tokenId) internal view returns(bool) {
  require(_to != address(0));
  require(_to != address(this));
  require(_owns(_from, _tokenId));
  require(_tokenId < cats.length);
  return (_spender == _from || approvedFor(_spender, _tokenId) || isApprovedForAll(_from, _spender));
}

//#######################################################################################################

// INTERNAL FUNCTION ASKING FOR ERC 721 SUPPORT  :
function _checkERC721Support(address _from, address _to, uint256 _tokenId, bytes memory _data) internal returns(bool) {
  if ( !_isContract(_to) ) {
    return true;
  }
  bytes4 returnData = IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
  // Call on onERC721Received in the _to contract
  return returnData == MAGIC_ERC721_RECEIVED;
  // Check on Return Value;
}

// IF SIZE = O =>ITS A USER WALLET, IF > 0 => ITS A CONTRACT :
function _isContract(address _to) internal view returns(bool) {
  uint32 size;
  assembly{
    size := extcodesize(_to)
  }
  return size > 0;
}
//###########################################################################################################

//MARKETPLACE FUNCTIONS:


function getOffer(uint256 _tokenId) public view returns
(
  address seller,
  uint256 price,
  uint256 index,
  uint256 tokenId,
  bool active
) {

  require(_isOnOffer(_tokenId) == true, "There is No Active Offer for this Cat");
  require(tokenIdToOffer[_tokenId].seller != address(0), "Token not on offer!");

/**
  seller = tokenIdToOffer[_tokenId].seller;
  price = tokenIdToOffer[_tokenId].price;
  index = tokenIdToOffer[_tokenId].index;
  tokenId = tokenIdToOffer[_tokenId].tokenId;
  active = tokenIdToOffer[_tokenId].active;
*/
  //another way of doing it is like below :

Offer storage offer = tokenIdToOffer[_tokenId];
return (
  offer.seller,
  offer.price,
  offer.index,
  offer.tokenId,
  offer.active
);

}


function setOffer(uint256 _price, uint256 _tokenId) external {
  require(_isKittyOwner(msg.sender, _tokenId), "You are not the owner of this Cat");
  require(_isOnOffer(_tokenId) == false, "There is already an Active Offer for this Cat so You cannot sell the same Token twice");
  require(isApprovedForAll(msg.sender, address(this)), "Contract needs to be authorized for selling Kitty in the Future");

Offer memory newOffer = Offer({
  seller : msg.sender,
  price : _price,
  tokenId : _tokenId,
  index : offers.length,
  active : true

});

 offers.push(newOffer);
 tokenIdToOffer[_tokenId] = newOffer;

//Emit Event MarketTransaction
emit MarketTransaction("Create Offer", msg.sender, _tokenId);
}


function getAllTokenOnSale() external view  returns(uint256[] memory ) {

  uint256 totalOffers = offers.length;
// Filip's way:
  if (totalOffers == 0) {
   return new uint256[](0);//SIZE 1,2,3,4,5,
  }
 else {
/**
  uint256[] memory allOfferIds = new uint256[](totalOffers);
  uint256 i;
  uint256 activeOffers = 0;
  for (i = 0; i < totalOffers; i++) {
    if (offers[i].active == true) {
      allOfferIds[activeOffers] = offers[i].tokenId;
      activeOffers = activeOffers.add(1);
    }
  }

  if (activeOffers == totalOffers) return allOfferIds;

  //Else return a correctly sized array of only Active offers
  uint256[] memory activeOfferIds = new uint[](activeOffers);
  for (i = 0; i < activeOffers; i++ ) {
    activeOfferIds[i] = allOfferIds[i];
  }
return activeOfferIds;
*/
//Continuation of Filip's way :
  uint256[] memory result = new uint256[](totalOffers);
  uint256 offerId;

  for (offerId = 0; offerId < totalOffers; offerId ++) {
    if(offers[offerId].active == true) {
      result[offerId] = offers[offerId].tokenId;
    }
  }

  return result;

  }
}



function removeOffer(uint256 _tokenId) external {
  require(_isOnOffer(_tokenId) == true, "There is No Active Offer against this Cat");
  require(msg.sender == tokenIdToOffer[_tokenId].seller, "Only Seller can Remove Offer");

  //_removeOffer(_tokenId);
  // or We can use below two lines of code to ensure that the Offer is removed both from the Offer mapping i.e tokenIdToOffer and also from the offers array
  delete(tokenIdToOffer[_tokenId]);
  offers[tokenIdToOffer[_tokenId].index].active = false;


  //Emit Market Transaction event
  emit MarketTransaction("Remove Offer", msg.sender, _tokenId);
}



function buyKitty(uint256 _tokenId) external payable {
  Offer memory offer = tokenIdToOffer[_tokenId];
  require(_isOnOffer(_tokenId) == true, "This Cat is not for sale as there is No Active Offer against this");
  require(msg.value >= offer.price, "Please send Full Token Price");

  // Delete the Mapping of the Tranferrable token
  // Set the Bool= active as false in the Offer array as well
  delete(tokenIdToOffer[_tokenId]);
  offers[offer.index].active = false;
  //_removeOffer(_tokenId);
  //transfer of the price to the seller
  //PUSH METHOD
  //if (offer.price > 0) {
    //offer.seller.transfer(offer.price);
  //IMPLEMENTED PULL METHOD AS BELOW
  if(msg.value > 0) {
    (bool success, ) = offer.seller.call.value(msg.value)("");
    require(success, "Payment to seller failed!");
    }

  //Finally transfer the Ownership of the Kitty using the transferFrom function in KittyContract:
  safeTransferFrom(offer.seller, msg.sender, _tokenId);

  //Emit MarketTransaction event
  emit MarketTransaction("Buy", msg.sender, _tokenId);
}


function _isKittyOwner(address claimant,uint256 tokenId)
    internal
    view
    returns (bool)
{
    return(ownerOf(tokenId) == claimant);
}


function _isOnOffer(uint256 tokenId) internal view returns (bool) {
    return(tokenIdToOffer[tokenId].active == true);
}


/* External Function:
** Checks if given tokenId is on sale or not; returning true if it is, false if not.
*/
function isTokenOnSale(uint256 tokenId) external view returns (bool) {
    return (_isOnOffer(tokenId));
}

/**
function _removeOffer(uint256 tokenId) internal {
    Offer memory toBeRemoved = tokenIdToOffer[tokenId];

    uint256 lastIndex = offers.length.sub(1);
    if (toBeRemoved.index < lastIndex) { // not the last offer in the array
        // Move last offer record (in array) to overwrite the offer to be removed
        Offer memory lastOffer = offers[lastIndex];
        lastOffer.index = toBeRemoved.index;       // poisition to which last offer record will be moved
        offers[toBeRemoved.index] = lastOffer;    // overwrite offer to be removed (with last offer record)
        tokenIdToOffer[lastOffer.tokenId] = lastOffer; // Update record in the token mapping
        }
    offers.pop();   // remove last offer record (now redundant as moved, or is the offer to be removed)
    delete tokenIdToOffer[toBeRemoved.tokenId];
    }

*/



}```

And Below is the KittycontractUpdated.sol code :point_down:

pragma solidity 0.5.12;
import “./Kittycontract.sol”;

contract KittycontractUpdated is Kittycontract {

constructor() public {
init(msg.sender);
}

function init(address _owner) public onlyOwner {
require(!_initialized);
contractOwner = _owner;
_initialized = true;
}

}```

and Finally the Proxy.sol code :point_down:

pragma solidity 0.5.12;
import "./Storage.sol";

contract Proxy is Storage {

  address currentAddress;

  constructor(address _currentAddress) public {
    contractOwner = msg.sender;
    currentAddress = _currentAddress;
  }


  function upgrade(address _newAddress) public  {
    require(msg.sender == contractOwner);
    currentAddress = _newAddress;
  }

  //FALLBACK FUNCTION
  function() payable external {
    // Redirect to currentAddress;
    address implementation = currentAddress;
    require(currentAddress != address(0));
    bytes memory data = msg.data;

    //DELEGATECALL EVERY FUNCTION CALL
    assembly {
      let result := delegatecall(gas, implementation, add(data, 0x20), mload(data), 0,0)
      let size := returndatasize
      let ptr := mload(0x40)
      returndatacopy(ptr,0,size)
      switch result
      case 0 {revert(ptr,size)}
      default {return(ptr,size)}
    }
  }


}

**Can someone Please help me figure out how to Deploy this on a Testnet ? Also, because when i migrate --reset in Truffle, it only deploys and displays the Migrations address and deploys(but doesn’t mention/ provide a contract address for Kittycontract " :point_down:


Starting migrations...
======================
> Network name:    'develop'
> Network id:      5777
> Block gas limit: 0x6691b7


1_initial_migration.js
======================

   Replacing 'Migrations'
   ----------------------
   > transaction hash:    0xdb92ed409ec870d4922931e51b50cc9042a34273a98db7d10263b3517ff495c6
   > Blocks: 0            Seconds: 0
   > contract address:    0x8951C4B3781b7B48C65c9B3946e1479683B0bb99
   > block number:        1
   > block timestamp:     1623674130
   > account:             0x7cf327BAaf52fc9E43221d1039180E0fd6B9C75F
   > balance:             99.9955689
   > gas used:            221555
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.0044311 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0044311 ETH


2_deploy_Kittycontract.js
=========================
After Creation of 1 Kitty, Result is  : 1 Kitty

   > Saving migration to chain.
   -------------------------------------
   > Total cost:                   0 ETH


Summary
=======
> Total deployments:   1
> Final cost:          0.0044311 ETH


truffle(develop)> 

Please someone Help me how to proceed further and what to do, in Order to interact with my Front end ?
Thanks and Regards

Suveett Kalra

(Su.kal Crypto)

1 Like

Wow, I could go quite deep into this as I did a complete redesign for upgradeability of an NFT project just recently. But this would truly be content for a whole course. I can advise you to join the openzeppelin forum and ask your questions there, you will surely get help.
Did you do unit testing on your upgradeable design?

Hi @thecil @Bhujanga @dan-i @filip
Filip with your due permissions, I would like to address to @Bhujanga @dan-i @thecil (In case they are not full time employees of the academy)

You guys are really smart in coding ( I am just a 6 month old stupid Non technical background Coder who is just trying to catch up with Hard work) ==> I have an interesting proposal (in case any one of you is Interested and in case you are permitted by the academy)
I don’t know if this Forum allows this kind of Discussions, But just asking anyways with Filip’s permissions :point_down:

My Friend Tarun Jaswani ( who stays in my Neighbourhood in New Delhi, India ) and my Friend Shashwat ( co owner of Altcoin Buzz- YouTube Channel and located in Singapore) had both launched Unbound Finance a new age DeFi protocol with a synthetic asset Unbound Token (issued by depositing UniV3 NFT based LP tokens) and the best Part of this protocol is their Math (I have seen the beta testing wherein Liquidation is greatly reduced even if Ether value falls by 80% in that LP token) , so this generates a lot of Interest in DeFi , especially for LP providers) and just because of this problem solving they got recently a 6 million funding round from Pantera Capital, Andreessen Horowitz and others.

They are looking out for Solidity engineers ( remote) on Freelance basis who can contribute to their project >

Would anyone amongst you be interested ? Kindly let me know, i think it could be a great experience learning and contributing ?

I can create a Whatsapp group between all and we can take it forward transparently. I just want to help them ( really genuine Solid awesome guys) .
my Mobile is +919810187088 and you can all contact me.

Thanks and Regards
Suveett Kalra
(Su.kal Crypto)
@Suveett_Crypto (India’s Crypto Man) on Twitter

2 Likes

Hey guys, I just have a random question from what Filip said in the video
that if we will declare a state variable on DogsUpdated contract that is not found on the proxy contract,
that it will be dangerous because it might corrupt our storage on the proxy,
I did try to declare “uint256 test;”
on the DogsUpdated contract and run migrate --reset and I haven’t had issues, still show the same result as the previous one.

Am I missing something? or can someone explain to me and give me example how will it corrupt our contracts? Thanks guys.

Hi @Bhujanga

Yes, I did and it’s working fine., except for one Test.
Would you mind if i send you a Github Repo link and you can let me know what / where I am going wrong ?

Also, I am really sorry I just noticed your email regarding Cooperation on the unbound Project?

send me your Number, i will contact you … and also connect you with Tarun / Shashwat. Rest all is your capability/ skills/ Hardwork
Cheers ad Best of Luck Buddy

Thanks and Regards

Suveett Kalra
Su.Kal Crypto

1 Like

Sure drop just drop the link here. I will have a look :slight_smile:

Hi @Bhujanga

Here you go, This is the Github Link:
https://github.com/Suveett/CryptoKitties.git
Please download the CryptoKittiesUpgradeable.zip file(which contains the Proxy.sol, Storage.sol, Kittycontract.sol and KittycontractUpdated.sol) and not the CryptoKitties.zip file (This is the normal Project)

I have done the Unit testing in the deployer only (although its not recommended, but Honestly i also couldn’t figure out how to implement a Unit testing on a Kittycontract through a Proxy Interaction without deploying ? )

But now, all the Tests are actually working Fine, thats my biggest relief, Just have a look if you can add some points and make this project Better and more Knowledgeable / Informative for me as well as you, perhaps. Or Kindly enlighten me if I have absolutely followed the wrong approach and maybe I should have followed a Newer better approach

Also, I am not so strong in JS, so somehow (inspite of several trials), my Kitties are not showing on my Marketplace.html page and catalogue.html page ( and the funny part is that the Console is showing No errors also, so maybe its a matter of visibility and not functionality - which i am unable to figure out- So Please help me if you can while you scroll through the Project).

On the Blockchain side (in solidity), everything is working/ compiling well.

One More thing I have not been able to understand yet is that while my Truffle shows only 4 deployments as I had intended - migrations, ArrayUtils, Kittycontract and Proxy), my Ganache shows 5 deployments (including the KittycontractUpdated.sol )
Can you shed some light on this ? :point_down: :thinking:
Screenshot 2021-06-25 at 1.46.07 PM
Screenshot 2021-06-25 at 1.57.57 PM

But since migrate --reset , My Ganache has hibernated and non -responsive .
Maybe this Project is not supposed to be done on ganache :smile: :thinking: ?? I am just curious to learn More.
Please enlighten me

Thanks a ton in advance and Best Regards
Suveett Kalra

P.S. - Also, please leave your number (whatsapp), or your telegram name or discord name. This is the only way i can ask Tarun to get in touch with you
Cheers

I stoped using ganache GUI for the same reason, some times it goes buggy, it close it self, stop responding, so i have tried ganache CLI, which is through a command line, also truffle develop creates an instance of a local blockchain, or hardhat node does the same trick.

Carlos Z

1 Like

Hi @Mariano_Collarte , I don’t see how this resolves your original concern? I like your suggestion of resetting _initialized in upgrade().

In my opinion, calling the initialize function a second time from the proxy (i.e. after another update of the functional contract) would be reverted because the _initialized variable of the proxy has already been set to true after the first contract update and is never reset to false. Thus the statement require(!_initialized); in the initialize function would fail, wouldn’t it?

@dan-i or @thecil, could you please elaborate on that?

I’ve already looked at the link @dan-i shared with @Michal_Kosior on the same question: https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#the-constructor-caveat
My concern still exists. Am I missing something?