Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save solalette/a632cb0bff518737327abc1751834de4 to your computer and use it in GitHub Desktop.
Save solalette/a632cb0bff518737327abc1751834de4 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev The contract has an owner address, and provides basic authorization control whitch
* simplifies the implementation of user permissions. This contract is based on the source code at:
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol
*/
contract Ownable
{
/**
* @dev Error constants.
*/
string public constant NOT_CURRENT_OWNER = "018001";
string public constant CANNOT_TRANSFER_TO_ZERO_ADDRESS = "018002";
/**
* @dev Current owner address.
*/
address public owner;
/**
* @dev An event which is triggered when the owner is changed.
* @param previousOwner The address of the previous owner.
* @param newOwner The address of the new owner.
*/
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The constructor sets the original `owner` of the contract to the sender account.
*/
constructor()
{
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner()
{
require(msg.sender == owner, NOT_CURRENT_OWNER);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(
address _newOwner
)
public
onlyOwner
{
require(_newOwner != address(0), CANNOT_TRANSFER_TO_ZERO_ADDRESS);
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Optional metadata extension for ERC-721 non-fungible token standard.
* See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md.
*/
interface ERC721Metadata
{
/**
* @dev Returns a descriptive name for a collection of NFTs in this contract.
* @return _name Representing name.
*/
function name()
external
view
returns (string memory _name);
/**
* @dev Returns a abbreviated name for a collection of NFTs in this contract.
* @return _symbol Representing symbol.
*/
function symbol()
external
view
returns (string memory _symbol);
/**
* @dev Returns a distinct Uniform Resource Identifier (URI) for a given asset. It Throws if
* `_tokenId` is not a valid NFT. URIs are defined in RFC3986. The URI may point to a JSON file
* that conforms to the "ERC721 Metadata JSON Schema".
* @return URI of _tokenId.
*/
function tokenURI(uint256 _tokenId)
external
view
returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev ERC-721 interface for accepting safe transfers.
* See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md.
*/
interface ERC721TokenReceiver
{
/**
* @notice The contract address is always the message sender. A wallet/broker/auction application
* MUST implement the wallet interface if it will accept safe transfers.
* @dev Handle the receipt of a NFT. The ERC721 smart contract calls this function on the
* recipient after a `transfer`. This function MAY throw to revert and reject the transfer. Return
* of other than the magic value MUST result in the transaction being reverted.
* Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` unless throwing.
* @param _operator The address which called `safeTransferFrom` function.
* @param _from The address which previously owned the token.
* @param _tokenId The NFT identifier which is being transferred.
* @param _data Additional data with no specified format.
* @return Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
*/
function onERC721Received(
address _operator,
address _from,
uint256 _tokenId,
bytes calldata _data
)
external
returns(bytes4);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev ERC-721 non-fungible token standard.
* See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md.
*/
interface ERC721
{
/**
* @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are
* created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any
* number of NFTs may be created and assigned without emitting Transfer. At the time of any
* transfer, the approved address for that NFT (if any) is reset to none.
*/
event Transfer(
address indexed _from,
address indexed _to,
uint256 indexed _tokenId
);
/**
* @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero
* address indicates there is no approved address. When a Transfer event emits, this also
* indicates that the approved address for that NFT (if any) is reset to none.
*/
event Approval(
address indexed _owner,
address indexed _approved,
uint256 indexed _tokenId
);
/**
* @dev This emits when an operator is enabled or disabled for an owner. The operator can manage
* all NFTs of the owner.
*/
event ApprovalForAll(
address indexed _owner,
address indexed _operator,
bool _approved
);
/**
* @notice Throws unless `msg.sender` is the current owner, an authorized operator, or the
* approved address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is
* the zero address. Throws if `_tokenId` is not a valid NFT. When transfer is complete, this
* function checks if `_to` is a smart contract (code size > 0). If so, it calls
* `onERC721Received` on `_to` and throws if the return value is not
* `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
* @dev Transfers the ownership of an NFT from one address to another address. This function can
* be changed to payable.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
* @param _data Additional data with no specified format, sent in call to `_to`.
*/
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
)
external;
/**
* @notice This works identically to the other function with an extra data parameter, except this
* function just sets data to ""
* @dev Transfers the ownership of an NFT from one address to another address. This function can
* be changed to payable.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
*/
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
external;
/**
* @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else
* they may be permanently lost.
* @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
* address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is the zero
* address. Throws if `_tokenId` is not a valid NFT. This function can be changed to payable.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
*/
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external;
/**
* @notice The zero address indicates there is no approved address. Throws unless `msg.sender` is
* the current NFT owner, or an authorized operator of the current owner.
* @param _approved The new approved NFT controller.
* @dev Set or reaffirm the approved address for an NFT. This function can be changed to payable.
* @param _tokenId The NFT to approve.
*/
function approve(
address _approved,
uint256 _tokenId
)
external;
/**
* @notice The contract MUST allow multiple operators per owner.
* @dev Enables or disables approval for a third party ("operator") to manage all of
* `msg.sender`'s assets. It also emits the ApprovalForAll event.
* @param _operator Address to add to the set of authorized operators.
* @param _approved True if the operators is approved, false to revoke approval.
*/
function setApprovalForAll(
address _operator,
bool _approved
)
external;
/**
* @dev Returns the number of NFTs owned by `_owner`. NFTs assigned to the zero address are
* considered invalid, and this function throws for queries about the zero address.
* @notice Count all NFTs assigned to an owner.
* @param _owner Address for whom to query the balance.
* @return Balance of _owner.
*/
function balanceOf(
address _owner
)
external
view
returns (uint256);
/**
* @notice Find the owner of an NFT.
* @dev Returns the address of the owner of the NFT. NFTs assigned to the zero address are
* considered invalid, and queries about them do throw.
* @param _tokenId The identifier for an NFT.
* @return Address of _tokenId owner.
*/
function ownerOf(
uint256 _tokenId
)
external
view
returns (address);
/**
* @notice Throws if `_tokenId` is not a valid NFT.
* @dev Get the approved address for a single NFT.
* @param _tokenId The NFT to find the approved address for.
* @return Address that _tokenId is approved for.
*/
function getApproved(
uint256 _tokenId
)
external
view
returns (address);
/**
* @notice Query if an address is an authorized operator for another address.
* @dev Returns true if `_operator` is an approved operator for `_owner`, false otherwise.
* @param _owner The address that owns the NFTs.
* @param _operator The address that acts on behalf of the owner.
* @return True if approved for all, false otherwise.
*/
function isApprovedForAll(
address _owner,
address _operator
)
external
view
returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./nf-token.sol";
import "./erc721-metadata.sol";
/**
* @dev Optional metadata implementation for ERC-721 non-fungible token standard.
*/
contract NFTokenMetadata is
NFToken,
ERC721Metadata
{
/**
* @dev A descriptive name for a collection of NFTs.
*/
string internal nftName;
/**
* @dev An abbreviated name for NFTokens.
*/
string internal nftSymbol;
/**
* @dev Mapping from NFT ID to metadata uri.
*/
mapping (uint256 => string) internal idToUri;
/**
* @notice When implementing this contract don't forget to set nftName and nftSymbol.
* @dev Contract constructor.
*/
constructor()
{
supportedInterfaces[0x5b5e139f] = true; // ERC721Metadata
}
/**
* @dev Returns a descriptive name for a collection of NFTokens.
* @return _name Representing name.
*/
function name()
external
override
view
returns (string memory _name)
{
_name = nftName;
}
/**
* @dev Returns an abbreviated name for NFTokens.
* @return _symbol Representing symbol.
*/
function symbol()
external
override
view
returns (string memory _symbol)
{
_symbol = nftSymbol;
}
/**
* @dev A distinct URI (RFC 3986) for a given NFT.
* @param _tokenId Id for which we want uri.
* @return URI of _tokenId.
*/
function tokenURI(
uint256 _tokenId
)
external
override
view
validNFToken(_tokenId)
returns (string memory)
{
return _tokenURI(_tokenId);
}
/**
* @notice This is an internal function that can be overriden if you want to implement a different
* way to generate token URI.
* @param _tokenId Id for which we want uri.
* @return URI of _tokenId.
*/
function _tokenURI(
uint256 _tokenId
)
internal
virtual
view
returns (string memory)
{
return idToUri[_tokenId];
}
/**
* @notice This is an internal function which should be called from user-implemented external
* burn function. Its purpose is to show and properly initialize data structures when using this
* implementation. Also, note that this burn implementation allows the minter to re-mint a burned
* NFT.
* @dev Burns a NFT.
* @param _tokenId ID of the NFT to be burned.
*/
function _burn(
uint256 _tokenId
)
internal
override
virtual
{
super._burn(_tokenId);
delete idToUri[_tokenId];
}
/**
* @notice This is an internal function which should be called from user-implemented external
* function. Its purpose is to show and properly initialize data structures when using this
* implementation.
* @dev Set a distinct URI (RFC 3986) for a given NFT ID.
* @param _tokenId Id for which we want URI.
* @param _uri String representing RFC 3986 URI.
*/
function _setTokenUri(
uint256 _tokenId,
string memory _uri
)
internal
validNFToken(_tokenId)
{
idToUri[_tokenId] = _uri;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./erc721.sol";
import "./erc721-token-receiver.sol";
import "../utils/supports-interface.sol";
import "../utils/address-utils.sol";
/**
* @dev Implementation of ERC-721 non-fungible token standard.
*/
contract NFToken is
ERC721,
SupportsInterface
{
using AddressUtils for address;
/**
* @dev List of revert message codes. Implementing dApp should handle showing the correct message.
* Based on 0xcert framework error codes.
*/
string constant ZERO_ADDRESS = "003001";
string constant NOT_VALID_NFT = "003002";
string constant NOT_OWNER_OR_OPERATOR = "003003";
string constant NOT_OWNER_APPROVED_OR_OPERATOR = "003004";
string constant NOT_ABLE_TO_RECEIVE_NFT = "003005";
string constant NFT_ALREADY_EXISTS = "003006";
string constant NOT_OWNER = "003007";
string constant IS_OWNER = "003008";
/**
* @dev Magic value of a smart contract that can receive NFT.
* Equal to: bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")).
*/
bytes4 internal constant MAGIC_ON_ERC721_RECEIVED = 0x150b7a02;
/**
* @dev A mapping from NFT ID to the address that owns it.
*/
mapping (uint256 => address) internal idToOwner;
/**
* @dev Mapping from NFT ID to approved address.
*/
mapping (uint256 => address) internal idToApproval;
/**
* @dev Mapping from owner address to count of their tokens.
*/
mapping (address => uint256) private ownerToNFTokenCount;
/**
* @dev Mapping from owner address to mapping of operator addresses.
*/
mapping (address => mapping (address => bool)) internal ownerToOperators;
/**
* @dev Guarantees that the msg.sender is an owner or operator of the given NFT.
* @param _tokenId ID of the NFT to validate.
*/
modifier canOperate(
uint256 _tokenId
)
{
address tokenOwner = idToOwner[_tokenId];
require(
tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender],
NOT_OWNER_OR_OPERATOR
);
_;
}
/**
* @dev Guarantees that the msg.sender is allowed to transfer NFT.
* @param _tokenId ID of the NFT to transfer.
*/
modifier canTransfer(
uint256 _tokenId
)
{
address tokenOwner = idToOwner[_tokenId];
require(
tokenOwner == msg.sender
|| idToApproval[_tokenId] == msg.sender
|| ownerToOperators[tokenOwner][msg.sender],
NOT_OWNER_APPROVED_OR_OPERATOR
);
_;
}
/**
* @dev Guarantees that _tokenId is a valid Token.
* @param _tokenId ID of the NFT to validate.
*/
modifier validNFToken(
uint256 _tokenId
)
{
require(idToOwner[_tokenId] != address(0), NOT_VALID_NFT);
_;
}
/**
* @dev Contract constructor.
*/
constructor()
{
supportedInterfaces[0x80ac58cd] = true; // ERC721
}
/**
* @notice Throws unless `msg.sender` is the current owner, an authorized operator, or the
* approved address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is
* the zero address. Throws if `_tokenId` is not a valid NFT. When transfer is complete, this
* function checks if `_to` is a smart contract (code size > 0). If so, it calls
* `onERC721Received` on `_to` and throws if the return value is not
* `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
* @dev Transfers the ownership of an NFT from one address to another address. This function can
* be changed to payable.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
* @param _data Additional data with no specified format, sent in call to `_to`.
*/
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
)
external
override
{
_safeTransferFrom(_from, _to, _tokenId, _data);
}
/**
* @notice This works identically to the other function with an extra data parameter, except this
* function just sets data to "".
* @dev Transfers the ownership of an NFT from one address to another address. This function can
* be changed to payable.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
*/
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
override
{
_safeTransferFrom(_from, _to, _tokenId, "");
}
/**
* @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else
* they may be permanently lost.
* @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
* address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is the zero
* address. Throws if `_tokenId` is not a valid NFT. This function can be changed to payable.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
*/
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
override
canTransfer(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, NOT_OWNER);
require(_to != address(0), ZERO_ADDRESS);
_transfer(_to, _tokenId);
}
/**
* @notice The zero address indicates there is no approved address. Throws unless `msg.sender` is
* the current NFT owner, or an authorized operator of the current owner.
* @dev Set or reaffirm the approved address for an NFT. This function can be changed to payable.
* @param _approved Address to be approved for the given NFT ID.
* @param _tokenId ID of the token to be approved.
*/
function approve(
address _approved,
uint256 _tokenId
)
external
override
canOperate(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(_approved != tokenOwner, IS_OWNER);
idToApproval[_tokenId] = _approved;
emit Approval(tokenOwner, _approved, _tokenId);
}
/**
* @notice This works even if sender doesn't own any tokens at the time.
* @dev Enables or disables approval for a third party ("operator") to manage all of
* `msg.sender`'s assets. It also emits the ApprovalForAll event.
* @param _operator Address to add to the set of authorized operators.
* @param _approved True if the operators is approved, false to revoke approval.
*/
function setApprovalForAll(
address _operator,
bool _approved
)
external
override
{
ownerToOperators[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/**
* @dev Returns the number of NFTs owned by `_owner`. NFTs assigned to the zero address are
* considered invalid, and this function throws for queries about the zero address.
* @param _owner Address for whom to query the balance.
* @return Balance of _owner.
*/
function balanceOf(
address _owner
)
external
override
view
returns (uint256)
{
require(_owner != address(0), ZERO_ADDRESS);
return _getOwnerNFTCount(_owner);
}
/**
* @dev Returns the address of the owner of the NFT. NFTs assigned to the zero address are
* considered invalid, and queries about them do throw.
* @param _tokenId The identifier for an NFT.
* @return _owner Address of _tokenId owner.
*/
function ownerOf(
uint256 _tokenId
)
external
override
view
returns (address _owner)
{
_owner = idToOwner[_tokenId];
require(_owner != address(0), NOT_VALID_NFT);
}
/**
* @notice Throws if `_tokenId` is not a valid NFT.
* @dev Get the approved address for a single NFT.
* @param _tokenId ID of the NFT to query the approval of.
* @return Address that _tokenId is approved for.
*/
function getApproved(
uint256 _tokenId
)
external
override
view
validNFToken(_tokenId)
returns (address)
{
return idToApproval[_tokenId];
}
/**
* @dev Checks if `_operator` is an approved operator for `_owner`.
* @param _owner The address that owns the NFTs.
* @param _operator The address that acts on behalf of the owner.
* @return True if approved for all, false otherwise.
*/
function isApprovedForAll(
address _owner,
address _operator
)
external
override
view
returns (bool)
{
return ownerToOperators[_owner][_operator];
}
/**
* @notice Does NO checks.
* @dev Actually performs the transfer.
* @param _to Address of a new owner.
* @param _tokenId The NFT that is being transferred.
*/
function _transfer(
address _to,
uint256 _tokenId
)
internal
virtual
{
address from = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(from, _tokenId);
_addNFToken(_to, _tokenId);
emit Transfer(from, _to, _tokenId);
}
/**
* @notice This is an internal function which should be called from user-implemented external
* mint function. Its purpose is to show and properly initialize data structures when using this
* implementation.
* @dev Mints a new NFT.
* @param _to The address that will own the minted NFT.
* @param _tokenId of the NFT to be minted by the msg.sender.
*/
function _mint(
address _to,
uint256 _tokenId
)
internal
virtual
{
require(_to != address(0), ZERO_ADDRESS);
require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
_addNFToken(_to, _tokenId);
emit Transfer(address(0), _to, _tokenId);
}
/**
* @notice This is an internal function which should be called from user-implemented external burn
* function. Its purpose is to show and properly initialize data structures when using this
* implementation. Also, note that this burn implementation allows the minter to re-mint a burned
* NFT.
* @dev Burns a NFT.
* @param _tokenId ID of the NFT to be burned.
*/
function _burn(
uint256 _tokenId
)
internal
virtual
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(tokenOwner, _tokenId);
emit Transfer(tokenOwner, address(0), _tokenId);
}
/**
* @notice Use and override this function with caution. Wrong usage can have serious consequences.
* @dev Removes a NFT from owner.
* @param _from Address from which we want to remove the NFT.
* @param _tokenId Which NFT we want to remove.
*/
function _removeNFToken(
address _from,
uint256 _tokenId
)
internal
virtual
{
require(idToOwner[_tokenId] == _from, NOT_OWNER);
ownerToNFTokenCount[_from] -= 1;
delete idToOwner[_tokenId];
}
/**
* @notice Use and override this function with caution. Wrong usage can have serious consequences.
* @dev Assigns a new NFT to owner.
* @param _to Address to which we want to add the NFT.
* @param _tokenId Which NFT we want to add.
*/
function _addNFToken(
address _to,
uint256 _tokenId
)
internal
virtual
{
require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
idToOwner[_tokenId] = _to;
ownerToNFTokenCount[_to] += 1;
}
/**
* @dev Helper function that gets NFT count of owner. This is needed for overriding in enumerable
* extension to remove double storage (gas optimization) of owner NFT count.
* @param _owner Address for whom to query the count.
* @return Number of _owner NFTs.
*/
function _getOwnerNFTCount(
address _owner
)
internal
virtual
view
returns (uint256)
{
return ownerToNFTokenCount[_owner];
}
/**
* @dev Actually perform the safeTransferFrom.
* @param _from The current owner of the NFT.
* @param _to The new owner.
* @param _tokenId The NFT to transfer.
* @param _data Additional data with no specified format, sent in call to `_to`.
*/
function _safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
)
private
canTransfer(_tokenId)
validNFToken(_tokenId)
{
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, NOT_OWNER);
require(_to != address(0), ZERO_ADDRESS);
_transfer(_to, _tokenId);
if (_to.isContract())
{
bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
require(retval == MAGIC_ON_ERC721_RECEIVED, NOT_ABLE_TO_RECEIVE_NFT);
}
}
/**
* @dev Clears the current approval of a given NFT ID.
* @param _tokenId ID of the NFT to be transferred.
*/
function _clearApproval(
uint256 _tokenId
)
private
{
delete idToApproval[_tokenId];
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @notice Based on:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol
* Requires EIP-1052.
* @dev Utility library of inline functions on addresses.
*/
library AddressUtils
{
/**
* @dev Returns whether the target address is a contract.
* @param _addr Address to check.
* @return addressCheck True if _addr is a contract, false if not.
*/
function isContract(
address _addr
)
internal
view
returns (bool addressCheck)
{
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly { codehash := extcodehash(_addr) } // solhint-disable-line
addressCheck = (codehash != 0x0 && codehash != accountHash);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev A standard for detecting smart contract interfaces.
* See: https://eips.ethereum.org/EIPS/eip-165.
*/
interface ERC165
{
/**
* @dev Checks if the smart contract includes a specific interface.
* This function uses less than 30,000 gas.
* @param _interfaceID The interface identifier, as specified in ERC-165.
* @return True if _interfaceID is supported, false otherwise.
*/
function supportsInterface(
bytes4 _interfaceID
)
external
view
returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./erc165.sol";
/**
* @dev Implementation of standard for detect smart contract interfaces.
*/
contract SupportsInterface is
ERC165
{
/**
* @dev Mapping of supported intefraces. You must not set element 0xffffffff to true.
*/
mapping(bytes4 => bool) internal supportedInterfaces;
/**
* @dev Contract constructor.
*/
constructor()
{
supportedInterfaces[0x01ffc9a7] = true; // ERC165
}
/**
* @dev Function to check which interfaces are suported by this contract.
* @param _interfaceID Id of the interface.
* @return True if _interfaceID is supported, false otherwise.
*/
function supportsInterface(
bytes4 _interfaceID
)
external
override
view
returns (bool)
{
return supportedInterfaces[_interfaceID];
}
}
REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads for the very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.
SCRIPTS
The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
Scripts have full access to the web3.js and ethers.js libraries.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol";
import "https://github.com/0xcert/ethereum-erc721/src/contracts/ownership/ownable.sol";
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
contract DatabaseManager is ChainlinkClient, Ownable {
using Chainlink for Chainlink.Request;
uint256 public volume;
address private oracle;
bytes32 private jobId;
uint256 private fee;
int public check = 0;
// This would be completely private is a privacy smart contract
string private key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkaWQ6ZXRocjoweEY5Q0NGQjAzZTcwMGNjNDI5OTY3MjY5NTcxQ0JhQ0M2QUE1NmRmYWIiLCJpc3MiOiJ3ZWIzLXN0b3JhZ2UiLCJpYXQiOjE2NDUzMTExNTQzODUsIm5hbWUiOiJOb3RlYm9vayJ9.N2axGWFvDekcS7UToV4h1QFVxFaDqi-JaruviRPbtyk";
// Map of smart contract address to NFT address
mapping(address => mapping(uint256 => bool)) public authenticated;
constructor() {
setPublicChainlinkToken();
oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
jobId = "d5270d1c311941d0b08bead21fea7747";
fee = 0.1 * 10 ** 18; // (Varies by network and job)
}
/*
* Create a Chainlink request
*/
function sendPost(string calldata message, string calldata tag, uint256 NFT) public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Create the URL for request
// Set the URL to perform the GET request on
request.add("get", "https://webhook.site/277dd33b-d215-499f-a2f2-88d3f79da4bf");
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* No response needed
*/
function fulfill(bytes32 _requestId) public recordChainlinkFulfillment(_requestId) {}
// This allows a smart contract to write into the database for the specific NFT
function auth(address smart, uint256 NFT, NFTokenMetadata id) public {
address owner = id.ownerOf(NFT);
require(msg.sender == owner, "Sender does not own NFT");
authenticated[smart][NFT] = true;
}
// This allows a smart contract to write to the database
function write(uint256 NFT, string calldata tag, string calldata message) external {
require(authenticated[msg.sender][NFT] == true, "Database write has not been authenticated");
authenticated[msg.sender][NFT] = false;
sendPost(message, tag, NFT);
}
}
contract newNFT is NFTokenMetadata {
constructor() {
nftName = "Notebook NFT";
nftSymbol = "NB";
}
function mint(address tokenAdd, address _to, uint256 _tokenId, string calldata _uri) public {
IERC20 token = IERC20(address(tokenAdd));
require(address(token) == 0xECF4fB3CFAFa9d9b1E81426C9b316B1ad179D5bE);
uint balance = token.balanceOf(_to);
require(balance > 0, "No notebook in account");
token.transferFrom(_to, address(this), balance);
super._mint(_to, _tokenId);
super._setTokenUri(_tokenId, _uri);
}
}
contract ServiceContract {
// This contract belongs to company XYZ that does loans
address public owner;
constructor() {
owner = msg.sender;
}
function paid(uint256 NFT, DatabaseManager DB) public {
require(msg.sender == owner);
DB.write(NFT, "loan", "Paid Back Loan");
}
function defaulted(uint256 NFT, DatabaseManager DB) public {
require(msg.sender == owner);
DB.write(NFT, "loan", "Defaulted on Loan");
}
}
contract iDDemo is IERC20 {
address msgSender;
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
string public name = "notebook token";
string public symbol = "nb";
uint8 public decimals = 0;
function transfer(address recipient, uint amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool) {
if (sender == address(this)) {
//msgSender = msg.sender;
//require(msg.sender == 0xc34F286d59d9874f2Df87a07af4FA9aB687dcA1f, "unpermissioned access");
balanceOf[address(this)] += amount;
totalSupply += amount;
} else {
allowance[sender][msg.sender] -= amount;
}
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
function mint(uint amount) external {
require(msg.sender == address(this));
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}
contract iDInterface {
//iDDemo coinContract;
//address constant public thirdParty = 0x545238BA21F36B095468132B6854CaAf8b5367B3;
// A shards have x = 1
mapping (string => uint) public aShards;
// B shards have x = 2
mapping (string => uint) public bShards;
// C shards have x = 3
mapping (string => uint) public cShards;
//update as contracts redeployed
address constant public authAadd = 0xBe7A14132598052b1F0809e3DdDdBD2a36d5C64D;
address constant public authBadd = 0x658a4c480C8D8dD08551b2767a291134Ce9f84e9;
address constant public authCadd = 0x3E04c34F0e1aD330960f4293938e49EDd092C55c;
// benchmark is all but the last 32 bytes of the address
function authShard(IERC20 token, uint shard, string calldata user, int authNum) public {
require((msg.sender == authAadd) || (msg.sender == authBadd) || (msg.sender == authCadd));
if (authNum == 0) {
aShards[user] = shard;
} else if (authNum == 1) {
bShards[user] = shard;
} else if (authNum == 2) {
cShards[user] = shard;
}
if ((aShards[user] != 0) && (bShards[user] != 0)) {
uint secret = 2 * aShards[user] - bShards[user];
uint160 add = uint160(secret);
authenticate(token, address(add), user);
} else if ((aShards[user] != 0) && (cShards[user] != 0)) {
uint secret = 3 * aShards[user] - cShards[user];
secret = secret / 2;
uint160 add = uint160(secret);
authenticate(token, address(add), user);
} else if ((bShards[user] != 0) && (cShards[user] != 0)) {
uint secret = 3 * bShards[user] - 2 * cShards[user];
uint160 add = uint160(secret);
authenticate(token, address(add), user);
}
}
//token is an address to a smart contract on the blockchain
function authenticate(IERC20 token, address verified, string calldata user) public {
//require(msg.sender == address(this), "not valid call");
//require((msg.sender == authAadd) || (msg.sender == authBadd) || (msg.sender == authCadd) || (msg.sender == address(this)));
// give the public address (verified) a token
token.transferFrom(address(token), verified, 1);
}
}
contract authA {
mapping (string => bool) public users;
function authenticate(address authManagerAdd, address tokenAdd, string calldata shardAStr, string calldata user) public {
uint shardA = 0;
bytes memory stringBytes = bytes(shardAStr);
for (uint i = 0; i < stringBytes.length; i++) {
uint exp = stringBytes.length - i;
bytes1 ival = stringBytes[i];
uint8 uval = uint8(ival);
uint jval = uval - uint(0x30);
shardA += (uint(jval) * (10**(exp-1)));
}
IERC20 token = IERC20(address(tokenAdd));
iDInterface authManager = iDInterface(address(authManagerAdd));
require(!users[user], "user already verified");
users[user] = true;
authManager.authShard(token, shardA, user, 0);
}
}
contract authB {
mapping (string => bool) public users;
function authenticate(address authManagerAdd, address tokenAdd, string calldata shardBStr, string calldata user) public {
uint shardB = 0;
bytes memory stringBytes = bytes(shardBStr);
for (uint i = 0; i < stringBytes.length; i++) {
uint exp = stringBytes.length - i;
bytes1 ival = stringBytes[i];
uint8 uval = uint8(ival);
uint jval = uval - uint(0x30);
shardB += (uint(jval) * (10**(exp-1)));
}
IERC20 token = IERC20(address(tokenAdd));
iDInterface authManager = iDInterface(address(authManagerAdd));
require(!users[user], "user already verified");
users[user] = true;
authManager.authShard(token, shardB, user, 1);
}
}
contract authC {
mapping (string => bool) public users;
function authenticate(address authManagerAdd, address tokenAdd, string calldata shardCStr, string calldata user) public {
uint shardC = 0;
bytes memory stringBytes = bytes(shardCStr);
for (uint i = 0; i < stringBytes.length; i++) {
uint exp = stringBytes.length - i;
bytes1 ival = stringBytes[i];
uint8 uval = uint8(ival);
uint jval = uval - uint(0x30);
shardC += (uint(jval) * (10**(exp-1)));
}
IERC20 token = IERC20(address(tokenAdd));
iDInterface authManager = iDInterface(address(authManagerAdd));
require(!users[user], "user already verified");
users[user] = true;
authManager.authShard(token, shardC, user, 2);
}
}
// Right click on the script name and hit "Run" to execute
(async () => {
try {
console.log('Running deployWithEthers script...')
const contractName = 'Storage' // Change this for other contract
const constructorArgs = [] // Put constructor args (if any) here for your contract
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer);
let contract = await factory.deploy(...constructorArgs);
console.log('Contract Address: ', contract.address);
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
console.log('Deployment successful.')
} catch (e) {
console.log(e.message)
}
})()
// Right click on the script name and hit "Run" to execute
(async () => {
try {
console.log('Running deployWithWeb3 script...')
const contractName = 'Storage' // Change this for other contract
const constructorArgs = [] // Put constructor args (if any) here for your contract
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({
data: metadata.data.bytecode.object,
arguments: constructorArgs
})
const newContractInstance = await contract.send({
from: accounts[0],
gas: 1500000,
gasPrice: '30000000000'
})
console.log('Contract deployed at address: ', newContractInstance.options.address)
} catch (e) {
console.log(e.message)
}
})()
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "../contracts/3_Ballot.sol";
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposal () public {
ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
}
function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment