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
Storage.sol
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
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
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 "
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)