Skip to content

Commit

Permalink
Merge pull request #10 from onflow/gio/add-base-erc721
Browse files Browse the repository at this point in the history
Add first ERC721 version + Solidity test suite
  • Loading branch information
sisyphusSmiling authored Oct 31, 2024
2 parents 85d798d + 60746be commit 07c2e0c
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
45 changes: 45 additions & 0 deletions solidity/src/MaybeMintERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
pragma solidity 0.8.24;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/**
* @title MaybeMintERC721
* @dev Mint ERC721 tokens for a fee in ERC20 tokens. As this simple token is intended for
* demonstration of batched EVM calls on Flow EVM using a single Cadence transaction, the
* minting process is random and has some chance of failure.
*/
contract MaybeMintERC721 is ERC721, Ownable {
IERC20 public denomination;
uint256 public mintCost;
address public beneficiary;
uint256 public totalSupply;

constructor(string memory _name, string memory _symbol, address _erc20, uint256 _mintCost, address _beneficiary)
ERC721(_name, _symbol)
Ownable(msg.sender)
{
denomination = IERC20(_erc20);
mintCost = _mintCost;
beneficiary = _beneficiary;
totalSupply = 0;
}

/**
* @dev Mint a new ERC721 token to the caller with some chance of failure. This is for
* demonstration purposes, intended to showcase how a single Cadence transaction can batch
* multiple EVM calls and condition final execution based on the result of any individual
* EVM call.
*
* NOTE: This contract address must be approved to transfer mintCost amount from the caller
* to the beneficiary before minting the ERC721 to pay for mint
*/
function mint() external {
// TODO: Get a random number to determine if the mint is successful
// TODO: Set token URI
totalSupply++; // increment the total supply
denomination.transferFrom(msg.sender, beneficiary, mintCost); // take payment for mint
_mint(msg.sender, totalSupply); // mint the token, assigning the next tokenId
}
}
15 changes: 15 additions & 0 deletions solidity/src/test/ExampleERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

contract ExampleERC20 is ERC20, ERC20Burnable, Ownable, ERC20Permit {
constructor() ERC20("NAME", "SYMBOL") Ownable(msg.sender) ERC20Permit("NAME") {}

function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
42 changes: 42 additions & 0 deletions solidity/test/MaybeMintERC721.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
pragma solidity 0.8.24;

import {Test} from "forge-std/Test.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {MaybeMintERC721} from "../src/MaybeMintERC721.sol";
import {ExampleERC20} from "../src/test/ExampleERC20.sol";

contract MaybeMintERC721Test is Test {
// Contracts
MaybeMintERC721 private erc721;
ExampleERC20 private erc20;

// MaybeMintERC721 parameters
address payable beneficiary = payable(address(100));
uint256 internal mintCost = 1 ether;
string internal name = "Maybe Mint ERC721 Test";
string internal symbol = "MAYBE";

// Test values
address payable internal user = payable(address(101));

function setUp() public virtual {
vm.deal(user, 10 ether);

erc20 = new ExampleERC20();
erc721 = new MaybeMintERC721(name, symbol, address(erc20), mintCost, beneficiary);
}

function test_mint() public {
erc20.mint(user, mintCost); // mint ERC20 to user

vm.prank(user);
erc20.approve(address(erc721), mintCost); // approve the ERC20 to be spent by MaybeMintERC721

vm.prank(user);
erc721.mint(); // mint ERC721 to user

assertEq(erc721.ownerOf(1), user); // user should own the ERC721 token
}
}

0 comments on commit 07c2e0c

Please sign in to comment.