Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add feralfile exhibition v4_2 & feralfile token contracts #41

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
20 changes: 13 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ merge: sol-merger-check
mkdir /tmp/sol-merger && mkdir ./contracts/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralfileArtworkV2.sol /tmp/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralfileArtworkV3.sol /tmp/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralfileArtworkV4_1.sol /tmp/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralfileArtworkV4_2.sol /tmp/sol-merger && \
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralfileEnglishAuction.sol /tmp/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralFileAirdropV1.sol /tmp/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/OwnerData.sol /tmp/sol-merger && \
sol-merger --export-plugin SPDXLicenseRemovePlugin ./contracts/FeralfileToken.sol /tmp/sol-merger && \
mv /tmp/sol-merger/FeralfileArtworkV2.sol ./contracts/sol-merger/FeralfileExhibitionV2.sol && \
mv /tmp/sol-merger/FeralfileArtworkV3.sol ./contracts/sol-merger/FeralfileExhibitionV3.sol && \
mv /tmp/sol-merger/FeralfileArtworkV4_1.sol ./contracts/sol-merger/FeralfileExhibitionV4.sol && \
mv /tmp/sol-merger/FeralfileArtworkV4_2.sol ./contracts/sol-merger/FeralfileExhibitionV4.sol && \
mv /tmp/sol-merger/FeralfileEnglishAuction.sol ./contracts/sol-merger/FeralfileEnglishAuction.sol && \
mv /tmp/sol-merger/FeralFileAirdropV1.sol ./contracts/sol-merger/FeralFileAirdropV1.sol && \
mv /tmp/sol-merger/OwnerData.sol ./contracts/sol-merger/OwnerData.sol
mv /tmp/sol-merger/OwnerData.sol ./contracts/sol-merger/OwnerData.sol && \
mv /tmp/sol-merger/FeralfileToken.sol ./contracts/sol-merger/FeralfileToken.sol

build-contract: check
npm install && \
Expand All @@ -39,14 +41,16 @@ build-contract: check
jq -r ".abi" build/contracts/FeralfileExhibitionV2.json > ./build/FeralfileExhibitionV2.abi && \
jq -r ".bytecode" build/contracts/FeralfileExhibitionV3.json > ./build/FeralfileExhibitionV3.bin && \
jq -r ".abi" build/contracts/FeralfileExhibitionV3.json > ./build/FeralfileExhibitionV3.abi && \
jq -r ".bytecode" build/contracts/FeralfileExhibitionV4_1.json > ./build/FeralfileExhibitionV4.bin && \
jq -r ".abi" build/contracts/FeralfileExhibitionV4_1.json > ./build/FeralfileExhibitionV4.abi && \
jq -r ".bytecode" build/contracts/FeralfileExhibitionV4_2.json > ./build/FeralfileExhibitionV4.bin && \
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
jq -r ".abi" build/contracts/FeralfileExhibitionV4_2.json > ./build/FeralfileExhibitionV4.abi && \
jq -r ".bytecode" build/contracts/FeralfileEnglishAuction.json > ./build/FeralfileEnglishAuction.bin && \
jq -r ".abi" build/contracts/FeralfileEnglishAuction.json > ./build/FeralfileEnglishAuction.abi && \
jq -r ".bytecode" build/contracts/FeralFileAirdropV1.json > ./build/FeralFileAirdropV1.bin && \
jq -r ".abi" build/contracts/FeralFileAirdropV1.json > ./build/FeralFileAirdropV1.abi && \
jq -r ".bytecode" build/contracts/OwnerData.json > ./build/OwnerData.bin && \
jq -r ".abi" build/contracts/OwnerData.json > ./build/OwnerData.abi
jq -r ".abi" build/contracts/OwnerData.json > ./build/OwnerData.abi && \
jq -r ".bytecode" build/contracts/FeralfileToken.json > ./build/FeralfileToken.bin && \
jq -r ".abi" build/contracts/FeralfileToken.json > ./build/FeralfileToken.abi

build: build-contract
mkdir -p ./go-binding/feralfile-exhibition-v2 && \
Expand All @@ -55,9 +59,11 @@ build: build-contract
mkdir -p ./go-binding/feralfile-english-auction && \
mkdir -p ./go-binding/feralfile-airdrop-v1 && \
mkdir -p ./go-binding/owner-data && \
mkdir -p ./go-binding/feralfile-token && \
abigen --abi ./build/FeralfileExhibitionV2.abi --bin ./build/FeralfileExhibitionV2.bin --pkg feralfilev2 -type FeralfileExhibitionV2 --out ./go-binding/feralfile-exhibition-v2/abi.go
abigen --abi ./build/FeralfileExhibitionV3.abi --bin ./build/FeralfileExhibitionV3.bin --pkg feralfilev3 -type FeralfileExhibitionV3 --out ./go-binding/feralfile-exhibition-v3/abi.go
abigen --abi ./build/FeralfileExhibitionV4.abi --bin ./build/FeralfileExhibitionV4.bin --pkg feralfilev4 -type FeralfileExhibitionV4 --out ./go-binding/feralfile-exhibition-v4/abi.go
abigen --abi ./build/FeralfileEnglishAuction.abi --bin ./build/FeralfileEnglishAuction.bin --pkg english_auction -type FeralfileEnglishAuction --out ./go-binding/feralfile-english-auction/abi.go
abigen --abi ./build/FeralFileAirdropV1.abi --bin ./build/FeralFileAirdropV1.bin --pkg airdropv1 -type FeralFileAirdropV1 --out ./go-binding/feralfile-airdrop-v1/abi.go && \
abigen --abi ./build/OwnerData.abi --bin ./build/OwnerData.bin --pkg ownerdata -type OwnerData --out ./go-binding/owner-data/abi.go
abigen --abi ./build/OwnerData.abi --bin ./build/OwnerData.bin --pkg ownerdata -type OwnerData --out ./go-binding/owner-data/abi.go && \
abigen --abi ./build/FeralfileToken.abi --bin ./build/FeralfileToken.bin --pkg feralfiletoken -type FeralfileToken --out ./go-binding/feralfile-token/abi.go
188 changes: 188 additions & 0 deletions contracts/FeralfileArtworkV4_2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { FeralfileExhibitionV4_1 } from "./FeralfileArtworkV4_1.sol";
import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";

contract FeralfileExhibitionV4_2 is FeralfileExhibitionV4_1 {
hvthhien marked this conversation as resolved.
Show resolved Hide resolved

error TokenIDNotFound();
error MintNotEnabled();

struct TokenInfo {
string thumbnail;
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
bytes parameters;
}

struct MintDataWithIndex {
uint256 seriesId;
uint256 tokenId;
address owner;
uint256 tokenIndex;
}

mapping(uint256 => TokenInfo) public _tokenInfos; // tokenID => tokenInfo
mapping(uint256 => uint256) public _tokenIndexes; // tokenID => tokenIndex

string public tokenPlaceholderURL;
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
string public tokenPlaceholderThubmnail;

constructor(
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
string memory name_,
string memory symbol_,
bool burnable_,
bool bridgeable_,
address signer_,
address vault_,
address costReceiver_,
string memory contractURI_,
uint256[] memory seriesIds_,
uint256[] memory seriesMaxSupplies_
)
FeralfileExhibitionV4_1(
name_,
symbol_,
burnable_,
bridgeable_,
signer_,
vault_,
costReceiver_,
contractURI_,
seriesIds_,
seriesMaxSupplies_
)
{}

/// @notice Mint new collection of Artwork
/// @dev the function iterates over the array of MintDataWithIndex to call the internal function mintArtworksWithIndex
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
/// @param data an array of MintDataWithIndex
function mintArtworksWithIndex(
MintDataWithIndex[] calldata data
) external onlyAuthorized {
if (!mintable) {
revert MintNotEnabled();
}

for (uint256 i = 0; i < data.length; i++) {
_tokenIndexes[data[i].tokenId] = data[i].tokenIndex;
_mintArtwork(data[i].seriesId, data[i].tokenId, data[i].owner);
}
}

/// @notice Update the parameters of an edition to a new value
function updateArtworkParameters(uint256 tokenId, string calldata thumbnail, bytes calldata parameters)
external
onlyAuthorized
{
if (!_exists(tokenId)) {
revert TokenIDNotFound();
}

_tokenInfos[tokenId] = TokenInfo(thumbnail, parameters);
}

/// @notice _buildArtworkData returns an object of artwork which would push to the actually artwork
/// @param parameters - the parameters for building artwork data
function _buildArtworkData(bytes memory parameters)
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
private
pure
returns (string memory)
{
return Base64.encode(parameters);
}

/// @notice _buildPreviewURL returns the preview url
/// @param parameters - the token paramters
function _buildPreviewURL(bytes memory parameters)
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
private
view
returns (string memory)
{
if (parameters.length == 0) {
return tokenPlaceholderURL;
}

return _buildIframe(_buildArtworkData(parameters), tokenBaseURI);
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
}

/// @notice _buildThumbnailURL returns the preview url
/// @param thumbnail - the token thumbnail
function _buildThumbnailURL(string memory thumbnail)
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
private
view
returns (string memory)
{
if (bytes(thumbnail).length == 0) {
return tokenPlaceholderThubmnail;
}

return thumbnail;
}

/// @notice _buildIframe returns a base64 encoded data for ff-frame
/// @param artworkData - the artwork data which would bring into the artwork
/// @param iframeURI - the artwork URL to be loaded into the iframe
function _buildIframe(string memory artworkData, string memory iframeURI)
hvthhien marked this conversation as resolved.
Show resolved Hide resolved
private
pure
returns (string memory)
{
return
Base64.encode(
abi.encodePacked(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [solhint] reported by reviewdog 🐶
GC: String exceeds 32 bytes

"<!DOCTYPE html><html lang=\"en\"><head><script> var defaultArtworkData= ",
artworkData,
"</script><script>",
"let allowOrigins={\"https://feralfile.com\":!0};function resizeIframe(t){let e=document.getElementById(\"mainframe\");t&&(e.style.minHeight=t+\"px\")}",
"function initData(){pushArtworkDataToIframe(defaultArtworkData)}function pushArtworkDataToIframe(t){t&&document.getElementById(\"mainframe\").contentWindow.postMessage(t,\"*\")}",
"function updateArtworkData(t){document.getElementById(\"mainframe\").contentWindow.postMessage(t,\"*\")}",
"window.addEventListener(\"message\",function(t){allowOrigins[t.origin]?\"update-artwork-data\"===t.data.type&&updateArtworkData(t.data.artworkData):\"object\"==typeof t.data&&\"resize-iframe\"===t.data.type&&resizeIframe(t.data.newHeight)});</script>",
"</head><body style=\"overflow-x:hidden;padding:0;margin:0;width: 100%;\" onload=\"initData()\">",
"<iframe id=\"mainframe\" style=\"display:block;padding:0;margin:0;border:none;width:100%;height:100vh;\" src=\"",
iframeURI,
"\"></iframe> </body></html>"
)
);
}

/// @notice _buildDataURL returns a base64 encoded data for ff-frame
/// @param tokenID - the token ID for building artwork data
function _buildDataURL(uint256 tokenID) private view returns (string memory) {
TokenInfo memory tokenInfo = _tokenInfos[tokenID];
uint256 tokenIndex = _tokenIndexes[tokenID];

string memory tokenName = string(
abi.encodePacked(name(), " #", tokenIndex+1)
);

string memory json = string(
abi.encodePacked(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [solhint] reported by reviewdog 🐶
GC: String exceeds 32 bytes

"{\"name\":\"", tokenName,
"\", \"external_url\":\"https://feralfile.com\", \"image\":\"", _buildThumbnailURL(tokenInfo.thumbnail),
"\", \"animation_url\":\"data:text/html;base64,", _buildPreviewURL(tokenInfo.parameters),
"\"}"
)
);

return string(
abi.encodePacked(
"data:application/json;base64,", Base64.encode(bytes(json))
)
);
}

/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
if (!_exists(tokenId)) {
revert TokenIDNotFound();
}

return _buildDataURL(tokenId);
}
}
33 changes: 33 additions & 0 deletions contracts/FeralfileToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Authorizable } from "./Authorizable.sol";

contract FeralfileToken is ERC20, Authorizable {

error InvalidOwnersAndAmounts();

// Constructor to initialize the token with a name and symbol
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}
hvthhien marked this conversation as resolved.
Show resolved Hide resolved

/// @notice Mint new tokens to an owner
/// @param owner The address of the owner to receive the minted tokens
/// @param amount The amount of tokens to mint
function mint(address owner, uint256 amount) external onlyAuthorized {
_mint(owner, amount);
}

/// @notice Mint tokens for multiple owners
/// @param owners An array of addresses to receive the minted tokens
/// @param amounts An array of amounts of tokens to mint for each respective owner
function batchMint(address[] calldata owners, uint256[] calldata amounts) external virtual onlyAuthorized {
if (owners.length != amounts.length) {
revert InvalidOwnersAndAmounts();
}

for (uint256 i = 0; i < owners.length; i++) {
_mint(owners[i], amounts[i]);
}
}
}
Loading
Loading