Full Smart Contract Upgradeability Discussion

Thank you so much dani!! I thought the assembly would be fine since it compiled without throwing errors but clearly it wasnt the case. I did look into open zeppeling but i guess not deep enough.
It works just fine now, so happy to move forward now ahahah. Cheers :beers:

1 Like

I realised I am confused about the migrations file purpose. As I understand it, it was a set of js instructions to correctly deploy a contract right ? But, am I supposed to re-run migrations if I need to upgrade the functional contract so as to have my proxy working with the right address ?? Why isnt Filip working with the deployed contract, instead of making it all through migrations ? I mean, say I launch an upgradeable smart contract right ? A few months later i come up with an improvement, so I decide to upgrade it, will I have to add code to migrations and re-deploy it all ? 
 Thats what confuses me. Why isnt Filip upgrading the address from a deployed proxy contract ?
Or is Filip just testing that the proxy works before deployment ?

If my question is hard to understand I can rephrase it, thanks!

Hi @ArgenFrank

The migration file sets the rules for your contract deployment.

Why isnt Filip working with the deployed contract, instead of making it all through migrations

It’s much faster to write some js code in the migration file instead of running the commands one by one in the truffle console.

I mean, say I launch an upgradeable smart contract right ? A few months later i come up with an improvement, so I decide to upgrade it, will I have to add code to migrations and re-deploy it all ?

No, you can call the functions in your contract by using the truffle console or etherscan.

Cheers,
Dani

1 Like

Hi,
i understand why its working, but i am not sure if its still working when we try to do another upgrade.

Assume that we create DogsUpdate2 and deploy in the same way. Then, when we call initialize function again, we will have variable _initialized already set to true, because we will use state of proxy contract. Shouldn’t we set _initialized to false somewhere? e.g. in upgrade function

1 Like

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