Skip to content

Commit

Permalink
Merge pull request #84 from RootstockCollective/develop
Browse files Browse the repository at this point in the history
Merging into nftEC branch
  • Loading branch information
bezerrablockchain authored Oct 24, 2024
2 parents 7b4bf07 + 86c9046 commit fa5bd74
Show file tree
Hide file tree
Showing 13 changed files with 495 additions and 102 deletions.
51 changes: 26 additions & 25 deletions contracts/NFT/ERC721UpgradableAirdroppable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,48 @@
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import {ERC721UpgradableNonTransferrable} from "./ERC721UpgradableNonTransferrable.sol";
import {ERC721UpgradableBase} from "./ERC721UpgradableBase.sol";

abstract contract ERC721UpgradableAirdroppable is ERC721UpgradableNonTransferrable {
struct AirdroppableStorage {
uint256 _nextTokenId;
uint256 _maxNftSupply;
}
struct AirdropRecipient {
address receiver;
string ipfsCid;
}
struct AirdroppableStorage {
uint256 _nextTokenId;
}

interface IAirdroppable {
event AirdropExecuted(uint256 numMinted);

function airdrop(AirdropRecipient[] calldata receivers) external;
}

abstract contract ERC721UpgradableAirdroppable is ERC721UpgradableBase, IAirdroppable {
// keccak256(abi.encode(uint256(keccak256("rootstock.storage.ERC721Airdroppable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant storageLocation =
bytes32 private constant STORAGE_LOCATION =
0xb0b7c350b577073edef3635128030d229b37650766e59f19e264b4b50f30a500;

function _getStorage() private pure returns (AirdroppableStorage storage $) {
assembly {
$.slot := storageLocation
$.slot := STORAGE_LOCATION
}
}

function __ERC721Airdroppable_init(uint256 maxNftSupply) internal onlyInitializing {
AirdroppableStorage storage $ = _getStorage();
$._maxNftSupply = maxNftSupply;
}

/**
* @dev Distributes tokens to a list of addresses.
*
* @param ipfsCids An array of content identifiers (IPFS CIDs) for the token URIs.
* @param airdropAddresses An array of addresses to receive the tokens.
* @param receivers an array of receivers with corresponding IPFS CIDS
*/

function airdrop(string[] calldata ipfsCids, address[] calldata airdropAddresses) public onlyOwner {
function airdrop(AirdropRecipient[] calldata receivers) external virtual override onlyOwner {
AirdroppableStorage storage $ = _getStorage();
require(ipfsCids.length == airdropAddresses.length, "Arrays must be of the same length");
require(airdropAddresses.length <= $._maxNftSupply, "Too many airdrops at once");
uint256 tokenId = $._nextTokenId;
unchecked {
for (uint256 i = 0; i < airdropAddresses.length; i++) {
tokenId++;
_safeMint(airdropAddresses[i], tokenId);
_setTokenURI(tokenId, ipfsCids[i]);
}
for (uint256 i = 0; i < receivers.length; i++) {
tokenId++;
AirdropRecipient calldata item = receivers[i];
_safeMint(item.receiver, tokenId);
_setTokenURI(tokenId, item.ipfsCid);
}
$._nextTokenId = tokenId;
emit AirdropExecuted(receivers.length);
}
}
11 changes: 6 additions & 5 deletions contracts/NFT/ERC721UpgradableBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ abstract contract ERC721UpgradableBase is
UUPSUpgradeable
{
function __ERC721UpgradableBase_init(
string calldata name,
string calldata symbol,
string memory name,
string memory symbol,
address initialOwner
) internal onlyInitializing {
__ERC721_init(name, symbol);
Expand All @@ -35,15 +35,15 @@ abstract contract ERC721UpgradableBase is
function _increaseBalance(
address account,
uint128 value
) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
) internal virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
super._increaseBalance(account, value);
}

function _update(
address to,
uint256 tokenId,
address auth
) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (address) {
) internal virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (address) {
return super._update(to, tokenId, auth);
}

Expand All @@ -52,6 +52,7 @@ abstract contract ERC721UpgradableBase is
)
public
view
virtual
override(ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable)
returns (bool)
{
Expand All @@ -60,7 +61,7 @@ abstract contract ERC721UpgradableBase is

function tokenURI(
uint256 tokenId
) public view override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) {
) public view virtual override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) {
return super.tokenURI(tokenId);
}
}
134 changes: 134 additions & 0 deletions contracts/NFT/OGFounders.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

import {ERC721UpgradableNonTransferrable} from "./ERC721UpgradableNonTransferrable.sol";

contract OGFounders is ERC721UpgradableNonTransferrable {
using Strings for uint8;

error WasNotEnoughStRIFToMint(uint stRIF);
error CouldNotGetVotes(string);
error CouldNotGetVotesBytes(bytes);
error OutOfTokens(uint256 maxSupply);
error ThisAddressAlreadyOwnsTheToken(address owner);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

address public stRIF;
uint256 public firstProposalDate;
// Counter for the total number of minted tokens
uint8 private _totalMinted;
// number of metadata files in the IPFS directory
uint8 private _maxSupply;

function initialize(
address initialOwner,
address stRIFAddress,
uint256 _firstProposalDate
) public initializer {
__ERC721UpgradableBase_init("OGFoundersRootstockCollective", "OGF", initialOwner);
stRIF = stRIFAddress;
firstProposalDate = _firstProposalDate;
_maxSupply = 150;
}

/**
* @dev Returns the number of tokens available for minting
*/
function tokensAvailable() public view virtual returns (uint256) {
if (_totalMinted >= _maxSupply) return 0;
return _maxSupply - _totalMinted;
}

/**
* @dev Returns the token ID for a given owner address.
* This is a simplified version of the `tokenOfOwnerByIndex` function without the index
* parameter, since a community member can only own one token.
*/
function tokenIdByOwner(address owner) public view virtual returns (uint256) {
return tokenOfOwnerByIndex(owner, 0);
}

/**
* @dev Returns the token IPFS URI for the given owner address.
* This utility function combines two view functions.
*/
function tokenUriByOwner(address owner) public view virtual returns (string memory) {
return tokenURI(tokenIdByOwner(owner));
}

function mint() external virtual {
address caller = _msgSender();
//5623028
try IVotes(stRIF).getPastVotes(caller, firstProposalDate) returns (uint _votes) {
if (_votes < 1) {
revert WasNotEnoughStRIFToMint(_votes);
}
// make sure we still have some CIDs for minting new tokens
if (tokensAvailable() == 0) revert OutOfTokens(_maxSupply);

// minting
uint8 tokenId = ++_totalMinted;
string memory fileName = string.concat(tokenId.toString(), ".json"); // 1.json, 2.json ...
_safeMint(caller, tokenId);
_setTokenURI(tokenId, fileName);
} catch Error(string memory reason) {
revert CouldNotGetVotes(reason);
} catch (bytes memory reason) {
revert CouldNotGetVotesBytes(reason);
}
}

function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {}

function _baseURI() internal pure override returns (string memory) {
return "ipfs://";
}

/**
* @dev Prevents the transfer and mint of tokens to addresses that already own one.
* Ensures that one address cannot own more than one token.
*/
function _update(address to, uint256 tokenId, address auth) internal override returns (address) {
// Disallow transfers by smart contracts, as only EOAs can be community members
// slither-disable-next-line tx-origin
if (_msgSender() != tx.origin) revert ERC721InvalidOwner(_msgSender());
// disallow transfers to members (excluding zero-address for enabling burning)
// disable minting more than one token
if (to != address(0) && balanceOf(to) > 0) revert ERC721InvalidOwner(to);
return super._update(to, tokenId, auth);
}

// overrides required

function transferFrom(address from, address to, uint256 tokenId) public virtual override {
super.transferFrom(from, to, tokenId);
}

function approve(address to, uint256 tokenId) public virtual override {
super.approve(to, tokenId);
}

function setApprovalForAll(address operator, bool approved) public virtual override {
super.setApprovalForAll(operator, approved);
}

function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
return super.tokenURI(tokenId);
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return super.supportsInterface(interfaceId);
}

function _increaseBalance(address account, uint128 value) internal override {
super._increaseBalance(account, value);
}
}
39 changes: 30 additions & 9 deletions contracts/NFT/OgFoundersEcosystemPartner.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,48 @@
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import {ERC721UpgradableAirdroppable} from "./ERC721UpgradableAirdroppable.sol";
import {ERC721UpgradableNonTransferrable} from "./ERC721UpgradableNonTransferrable.sol";

contract OgFoundersEcosystemPartner is ERC721UpgradableAirdroppable {
contract OgFoundersEcosystemPartner is ERC721UpgradableAirdroppable, ERC721UpgradableNonTransferrable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(
string calldata contractName,
string calldata symbol,
address initialOwner,
uint256 maxNftSupply
) public initializer {
__ERC721Airdroppable_init(maxNftSupply);
__ERC721UpgradableBase_init(contractName, symbol, initialOwner);
function initialize(address initialOwner) public initializer {
__ERC721UpgradableBase_init("OgFoundersEcosystemPartner", "OGFEP", initialOwner);
}

function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {}

function _baseURI() internal pure override returns (string memory) {
return "ipfs://";
}

/* Overrides required by Solidity */

function approve(
address to,
uint256 tokenId
) public virtual override(IERC721, ERC721Upgradeable, ERC721UpgradableNonTransferrable) {
super.approve(to, tokenId);
}

function setApprovalForAll(
address operator,
bool approved
) public virtual override(IERC721, ERC721Upgradeable, ERC721UpgradableNonTransferrable) {
super.setApprovalForAll(operator, approved);
}

function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override(IERC721, ERC721Upgradeable, ERC721UpgradableNonTransferrable) {
super.transferFrom(from, to, tokenId);
}
}
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dotent from 'dotenv'
import './tasks/updateIpfsFolder'
import './tasks/cancelProposal'
import './tasks/withdrawTreasury'
import './tasks/airdrop'

dotent.config()

Expand Down
26 changes: 26 additions & 0 deletions ignition/modules/OGFounders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules'

export const OGFoundersProxyModule = buildModule('OGFounders', m => {
// deploy implementation
const implementation = m.contract('OGFounders', [], { id: 'Implementation' })

// initializer parameters
const deployer = m.getAccount(0)
const stRIFAddress = m.getParameter('stRIFAddress')
const firstProposalDate = m.getParameter('firstProposalDate')

// deploy proxy
const proxy = m.contract('ERC1967Proxy', [
implementation,
m.encodeFunctionCall(implementation, 'initialize', [deployer, stRIFAddress, firstProposalDate], {
id: 'Proxy',
}),
])
const OGFounders = m.contractAt('OGFounders', proxy, {
id: 'Contract',
})

return { OGFounders }
})

export default OGFoundersProxyModule
17 changes: 3 additions & 14 deletions ignition/modules/OgFoundersEP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,19 @@ export const ogFoundersEpProxyModule = buildModule('OgFoundersEP', m => {
const implementation = m.contract('OgFoundersEcosystemPartner', [], { id: 'Implementation' })

// initializer parameters
const contractName = m.getParameter('contractName')
const contractSymbol = m.getParameter('symbol')
const deployer = m.getAccount(0)
const maxNftSupply = m.getParameter('maxNftSupply')

// deploy proxy
const proxy = m.contract('ERC1967Proxy', [
implementation,
m.encodeFunctionCall(
implementation,
'initialize',
[contractName, contractSymbol, deployer, maxNftSupply],
{ id: 'Proxy' },
),
m.encodeFunctionCall(implementation, 'initialize', [deployer], {
id: 'Proxy',
}),
])
const ogFoundersEp = m.contractAt('OgFoundersEcosystemPartner', proxy, {
id: 'Contract',
})

// Airdrop
const ipfsCids = m.getParameter('ipfsCids')
const airdropAddresses = m.getParameter('airdropAddresses')
m.call(ogFoundersEp, 'airdrop', [ipfsCids, airdropAddresses])

return { ogFoundersEp }
})

Expand Down
Loading

0 comments on commit fa5bd74

Please sign in to comment.