From 2ec10e924dea2daab8cd3fa3c98ca331831675b9 Mon Sep 17 00:00:00 2001 From: Delioos Date: Tue, 1 Oct 2024 17:31:17 +0700 Subject: [PATCH] feat: core contract --- .gitignore | 6 +++ .gitmodules | 12 ++++++ README.md | 3 ++ foundry.toml | 12 ++++-- lib/ccip | 1 + lib/chainlink | 1 + lib/chainlink-ccip | 1 + lib/openzeppelin-contracts | 1 + remappings.txt | 2 + src/ERC1155Core.sol | 85 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 121 insertions(+), 3 deletions(-) create mode 160000 lib/ccip create mode 160000 lib/chainlink create mode 160000 lib/chainlink-ccip create mode 160000 lib/openzeppelin-contracts create mode 100644 remappings.txt create mode 100644 src/ERC1155Core.sol diff --git a/.gitignore b/.gitignore index 85198aa..c41bc6e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,14 @@ out/ /broadcast/*/31337/ /broadcast/**/dry-run/ +# openzeppelin +lib/ + # Docs docs/ # Dotenv file .env + +# javascript files +**/*.js diff --git a/.gitmodules b/.gitmodules index 888d42d..db055ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,15 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "lib/chainlink"] + path = lib/chainlink + url = https://github.com/smartcontractkit/chainlink +[submodule "lib/chainlink-ccip"] + path = lib/chainlink-ccip + url = https://github.com/smartcontractkit/chainlink-ccip +[submodule "lib/ccip"] + path = lib/ccip + url = https://github.com/smartcontractkit/ccip diff --git a/README.md b/README.md index 7c35999..97e79f5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ TODO: provide architecture and a quick project overview +`ERC1155Core.sol` contains the main logic. +It's based on the (openzeppellin) ERC1155 non fungible fractionalized contract. +To enable cross chain interoperability, we need to add burn and mint. A transfer from a chain 'A' to a chain 'B' consists of burning the supply in the chain 'A' and minting it on the chain 'B' ensuring "uniqueness" of our data on all chains. kudos to (@andrej)[https://x.com/andrej_dev] for the chainlink bootcamp <3 diff --git a/foundry.toml b/foundry.toml index 25b918f..20c0a30 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,6 +1,12 @@ [profile.default] -src = "src" -out = "out" -libs = ["lib"] +src = 'src' +out = 'out' +libs = ['lib'] +solc = '0.8.24' +cache = true +force = true +optimizer = true +optimizer_runs = 200 +evm_version = 'cancun' # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/lib/ccip b/lib/ccip new file mode 160000 index 0000000..71799d2 --- /dev/null +++ b/lib/ccip @@ -0,0 +1 @@ +Subproject commit 71799d2bd470db14162bf60fc51f8479f6c4183b diff --git a/lib/chainlink b/lib/chainlink new file mode 160000 index 0000000..72a7d23 --- /dev/null +++ b/lib/chainlink @@ -0,0 +1 @@ +Subproject commit 72a7d232412f3f5602e5e6d73959615c3b6ba1c0 diff --git a/lib/chainlink-ccip b/lib/chainlink-ccip new file mode 160000 index 0000000..4248922 --- /dev/null +++ b/lib/chainlink-ccip @@ -0,0 +1 @@ +Subproject commit 424892240cd16d46aa2545fac093113d9e59bfd7 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..b72e3da --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit b72e3da0ec1f47e4a7911a4c06dc92e78c646607 diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 0000000..fcb6652 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,2 @@ +@openzeppelin/=lib/openzeppelin-contracts/ +@chainlink/=lib/chainlink/ diff --git a/src/ERC1155Core.sol b/src/ERC1155Core.sol new file mode 100644 index 0000000..674322a --- /dev/null +++ b/src/ERC1155Core.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {ERC1155Supply, ERC1155} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; +import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol"; + +/** + * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. + * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ +contract ERC1155Core is ERC1155Supply, OwnerIsCreator { + address internal s_issuer; + + // Optional mapping for token URIs + mapping(uint256 tokenId => string) private _tokenURIs; + + event SetIssuer(address indexed issuer); + + error ERC1155Core_CallerIsNotIssuerOrItself(address msgSender); + + modifier onlyIssuerOrItself() { + if (msg.sender != address(this) && msg.sender != s_issuer) { + revert ERC1155Core_CallerIsNotIssuerOrItself(msg.sender); + } + _; + } + + // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json + constructor(string memory uri_) ERC1155(uri_) {} + + function setIssuer(address _issuer) external onlyOwner { + s_issuer = _issuer; + + emit SetIssuer(_issuer); + } + + function mint(address _to, uint256 _id, uint256 _amount, bytes memory _data, string memory _tokenUri) + public + onlyIssuerOrItself + { + _mint(_to, _id, _amount, _data); + _tokenURIs[_id] = _tokenUri; + } + + function mintBatch( + address _to, + uint256[] memory _ids, + uint256[] memory _amounts, + bytes memory _data, + string[] memory _tokenUris + ) public onlyIssuerOrItself { + _mintBatch(_to, _ids, _amounts, _data); + for (uint256 i = 0; i < _ids.length; ++i) { + _tokenURIs[_ids[i]] = _tokenUris[i]; + } + } + + function burn(address account, uint256 id, uint256 amount) public onlyIssuerOrItself { + if (account != _msgSender() && !isApprovedForAll(account, _msgSender())) { + revert ERC1155MissingApprovalForAll(_msgSender(), account); + } + + _burn(account, id, amount); + } + + function burnBatch(address account, uint256[] memory ids, uint256[] memory amounts) public onlyIssuerOrItself { + if (account != _msgSender() && !isApprovedForAll(account, _msgSender())) { + revert ERC1155MissingApprovalForAll(_msgSender(), account); + } + + _burnBatch(account, ids, amounts); + } + + function uri(uint256 tokenId) public view override returns (string memory) { + string memory tokenURI = _tokenURIs[tokenId]; + + return bytes(tokenURI).length > 0 ? tokenURI : super.uri(tokenId); + } + + function _setURI(uint256 tokenId, string memory tokenURI) internal { + _tokenURIs[tokenId] = tokenURI; + emit URI(uri(tokenId), tokenId); + } +}