Skip to content

Commit

Permalink
Merge pull request #66 from nfthashi/v0.3.0
Browse files Browse the repository at this point in the history
V0.3.0
  • Loading branch information
taijusanagi committed Jun 17, 2022
2 parents 573ff29 + 59f1e4a commit 40585b8
Show file tree
Hide file tree
Showing 80 changed files with 1,504 additions and 855 deletions.
Binary file added .DS_Store
Binary file not shown.
8 changes: 2 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
on: push

jobs:
build:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ http://docs.nfthashi.com/
NFT Hashi is a NFT bridge protocol that support transferring the NFTs between the deferent chains. We provide two types of bridge system, Wrap Pattern and Native Support Pattern, that can be used for different use cases.

# How to use NFT Hashi

### Installation

`$ npm i @nfthashi/contracts`

### Usage

Once installed, you can use the contracts in the library by importing them:

```
pragma solidity ^0.8.0;
import "@nfthashi/contracts/contracts/native/example/NativeNFT";
import "@nfthashi/contracts/contracts/native/example/NativeHashi721";
contract MyCollectible is NativeNFT {
contract MyCollectible is NativeHashi721 {
constructor(
uint32 selfDomain,
address connext,
Expand All @@ -38,17 +42,18 @@ contract MyCollectible is NativeNFT {
}
}
```
- selfDomain :
The domain ID of the network you deploy
- connext :
The connext handler address of the network you deploy
- dummyTransactionAssetId :
The test ERC20 token address of the network you deploy
- startTokenId & endTokenId :
Enter how many tokens you want to mint in this chain
ex) You want to set token Id 101 ~ 200 to this network, startTokenId is 101 and endTokenId is 200
- name & symbol & baseTokenURI :
Enter each as you would when creating an ERC721

- selfDomain :
The domain ID of the network you deploy
- connext :
The connext handler address of the network you deploy
- dummyTransactionAssetId :
The test ERC20 token address of the network you deploy
- startTokenId & endTokenId :
Enter how many tokens you want to mint in this chain
ex) You want to set token Id 101 ~ 200 to this network, startTokenId is 101 and endTokenId is 200
- name & symbol & baseTokenURI :
Enter each as you would when creating an ERC721

You can find some necessary informations (ex domainID, connext address) from here
https://docs.nfthashi.com/developer-guide/informations
Expand All @@ -57,10 +62,10 @@ For example, when deploying to rinkeby with token ID 101 ~ 200 set, the argument
pragma solidity ^0.8.0;

```
import "@nfthashi/contracts/contracts/native/example/NativeNFT";
import "@nfthashi/contracts/contracts/native/example/NativeHashi721";
contract MyCollectible is NativeNFT {
constructor() NativeNFT(
contract MyCollectible is NativeHashi721 {
constructor() NativeHashi721(
1111,
a0x2307Ed9f152FA9b3DcDfe2385d279D8C2A9DF2b0,
0x3FFc03F05D1869f493c7dbf913E636C6280e0ff9,
Expand All @@ -73,6 +78,7 @@ contract MyCollectible is NativeNFT {
}
}
```

If you're new to smart contract development, head to Developing Smart Contracts to learn about creating a new project and compiling your contracts. This Openzeppelin Document help your understanding.

## How to bridge
Expand Down
19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nfthashi",
"version": "0.2.0",
"version": "0.3.0",
"license": "UNLICENSED",
"private": true,
"workspaces": [
Expand All @@ -10,23 +10,24 @@
"prepare": "husky install",
"ci": "run-p lint build test",
"lint": "run-p lint:*",
"lint:contracts": "yarn workspace contracts lint",
"lint:web": "yarn workspace web lint",
"lint:contracts": "yarn workspace @nfthashi/contracts lint",
"lint:web": "yarn workspace @nfthashi/web lint",
"fix": "run-p fix:*",
"fix:contracts": "yarn workspace contracts fix",
"fix:web": "yarn workspace web fix",
"fix:contracts": "yarn workspace @nfthashi/contracts fix",
"fix:web": "yarn workspace @nfthashi/web fix",
"test": "run-p test:*",
"test:contracts": "yarn workspace contracts test",
"test:contracts": "yarn workspace @nfthashi/contracts test",
"predev": "yarn build:contracts",
"dev": "run-p dev:*",
"dev:web": "yarn workspace web dev",
"dev:web": "yarn workspace @nfthashi/web dev",
"build": "run-s build:contracts build:web",
"build:contracts": "yarn workspace contracts build",
"build:web": "yarn workspace web build"
"build:contracts": "yarn workspace @nfthashi/contracts build",
"build:web": "yarn workspace @nfthashi/web build"
},
"devDependencies": {
"@commitlint/cli": "^16.2.4",
"@commitlint/config-conventional": "^16.2.4",
"cross-env": "^7.0.3",
"husky": "^8.0.1",
"lint-staged": "^12.4.1",
"npm-run-all": "^4.1.5"
Expand Down
5 changes: 0 additions & 5 deletions packages/contracts/.prettierignore

This file was deleted.

3 changes: 3 additions & 0 deletions packages/contracts/.solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
skipFiles: ["__faucet", "__mock"],
};
13 changes: 12 additions & 1 deletion packages/contracts/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", "^0.8.0"],
"func-visibility": ["warn", { "ignoreConstructors": true }]
"func-visibility": [
"warn",
{
"ignoreConstructors": true
}
],
"func-param-name-mixedcase": "error",
"modifier-name-mixedcase": "error",
"not-rely-on-time": "off",
"ordering": "error",
"private-vars-leading-underscore": "error",
"reason-string": "off"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import "@openzeppelin/contracts/interfaces/IERC721Metadata.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../interface/INFTWrapBridge.sol";

import "../core/NFTBridge.sol";
import "./WrappedNFT.sol";
import "./HashiConnextAdapter.sol";
import "./interfaces/IWrappedHashi721.sol";

contract NFTWrapBridge is ERC165, INFTWrapBridge, NFTBridge {
contract Hashi721Bridge is ERC165, HashiConnextAdapter {
mapping(address => address) private _contracts;
mapping(address => uint32) private _domains;

Expand All @@ -22,7 +21,7 @@ contract NFTWrapBridge is ERC165, INFTWrapBridge, NFTBridge {
address connext,
address dummyTransactingAssetId,
address nftImplementation
) NFTBridge(selfDomain, connext, dummyTransactingAssetId) {
) HashiConnextAdapter(selfDomain, connext, dummyTransactingAssetId) {
_nftImplementation = nftImplementation;
}

Expand All @@ -31,34 +30,40 @@ contract NFTWrapBridge is ERC165, INFTWrapBridge, NFTBridge {
address from,
address to,
uint256 tokenId,
uint32 sendToDomain
uint32 sendToDomain,
bool isTokenURIIncluded
) public {
require(
IERC165(processingNFTContractAddress).supportsInterface(type(IERC721).interfaceId),
"Hashi721Bridge: invalid nft"
);
require(
IERC721(processingNFTContractAddress).ownerOf(tokenId) == _msgSender() ||
IERC721(processingNFTContractAddress).getApproved(tokenId) == _msgSender() ||
IERC721(processingNFTContractAddress).isApprovedForAll(from, _msgSender()),
"NativeNFT: invalid sender"
"Hashi721Bridge: invalid sender"
);
require(IERC721(processingNFTContractAddress).ownerOf(tokenId) == from, "NativeNFT: invalid from");
require(IERC721(processingNFTContractAddress).ownerOf(tokenId) == from, "Hashi721Bridge: invalid from");

address birthChainNFTContractAddress;
uint32 birthChainDomain;
uint32 destinationDomain;

string memory name = IERC721Metadata(processingNFTContractAddress).name();
string memory symbol = IERC721Metadata(processingNFTContractAddress).symbol();
string memory tokenURI = IERC721Metadata(processingNFTContractAddress).tokenURI(tokenId);
string memory tokenURI;
if (isTokenURIIncluded) {
tokenURI = IERC721Metadata(processingNFTContractAddress).tokenURI(tokenId);
}

if (_contracts[processingNFTContractAddress] != address(0x0) && _domains[processingNFTContractAddress] != 0) {
birthChainNFTContractAddress = _contracts[processingNFTContractAddress];
birthChainDomain = _domains[processingNFTContractAddress];
destinationDomain = birthChainDomain;
WrappedNFT(processingNFTContractAddress).burn(tokenId);
} else {
if (_contracts[processingNFTContractAddress] == address(0x0) && _domains[processingNFTContractAddress] == 0) {
birthChainNFTContractAddress = processingNFTContractAddress;
birthChainDomain = getSelfDomain();
destinationDomain = sendToDomain;
IERC721(birthChainNFTContractAddress).transferFrom(from, address(this), tokenId);
} else {
birthChainNFTContractAddress = _contracts[processingNFTContractAddress];
birthChainDomain = _domains[processingNFTContractAddress];
destinationDomain = birthChainDomain;
IWrappedHashi721(processingNFTContractAddress).burn(tokenId);
}

bytes memory callData = abi.encodeWithSelector(
Expand All @@ -67,8 +72,6 @@ contract NFTWrapBridge is ERC165, INFTWrapBridge, NFTBridge {
to,
tokenId,
birthChainDomain,
name,
symbol,
tokenURI
);
_xcall(destinationDomain, callData);
Expand All @@ -79,8 +82,6 @@ contract NFTWrapBridge is ERC165, INFTWrapBridge, NFTBridge {
address to,
uint256 tokenId,
uint32 birthChainDomain,
string memory name,
string memory symbol,
string memory tokenURI
) public onlyExecutor {
uint32 selfDomain = getSelfDomain();
Expand All @@ -97,23 +98,9 @@ contract NFTWrapBridge is ERC165, INFTWrapBridge, NFTBridge {
Clones.cloneDeterministic(_nftImplementation, salt);
_contracts[processingNFTContractAddress] = birthChainNFTContractAddress;
_domains[processingNFTContractAddress] = birthChainDomain;
IWappedNFT(processingNFTContractAddress).initialize(name, symbol);
IWrappedHashi721(processingNFTContractAddress).initialize();
}
WrappedNFT(processingNFTContractAddress).mint(to, tokenId, tokenURI);
IWrappedHashi721(processingNFTContractAddress).mint(to, tokenId, tokenURI);
}
}

function isNFTHashiWrapBridge() public pure returns (bool) {
return true;
}

function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(NFTBridge, ERC165, IERC165)
returns (bool)
{
return interfaceId == type(INFTWrapBridge).interfaceId || super.supportsInterface(interfaceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ import "@connext/nxtp-contracts/contracts/libraries/LibConnextStorage.sol";
import "@connext/nxtp-contracts/contracts/interfaces/IExecutor.sol";
import "@connext/nxtp-contracts/contracts/interfaces/IConnextHandler.sol";

import "../interface/INFTBridge.sol";
import "hardhat/console.sol";

contract NFTBridge is Ownable, ERC165, INFTBridge {
contract HashiConnextAdapter is Ownable, ERC165 {
mapping(uint32 => address) private _bridgeContracts;

address private immutable _connext;
address private immutable _executor;
address private immutable _transactingAssetId;

uint32 private immutable _selfDomain;

event BridgeSet(uint32 domain, address bridgeContract);

modifier onlyExecutor() {
IExecutor(msg.sender).originSender();
require(msg.sender == _executor, "HashiConnextAdapter: sender invalid");
require(
IExecutor(msg.sender).originSender() == _bridgeContracts[IExecutor(msg.sender).origin()] &&
msg.sender == _executor,
"NFTBridge: invalid executor"
IExecutor(msg.sender).originSender() == _bridgeContracts[IExecutor(msg.sender).origin()],
"HashiConnextAdapter: origin sender invalid"
);
_;
}
Expand All @@ -42,11 +42,32 @@ contract NFTBridge is Ownable, ERC165, INFTBridge {

function setBridgeContract(uint32 domain, address bridgeContract) public onlyOwner {
_bridgeContracts[domain] = bridgeContract;
emit BridgeSet(domain, bridgeContract);
}

function getBridgeContract(uint32 domain) public view returns (address) {
return _bridgeContracts[domain];
}

function getConnext() public view returns (address) {
return _connext;
}

function getExecutor() public view returns (address) {
return _executor;
}

function getSelfDomain() public view returns (uint32) {
return _selfDomain;
}

function getTransactingAssetId() public view returns (address) {
return _transactingAssetId;
}

function _xcall(uint32 destinationDomain, bytes memory callData) internal {
address destinationContract = _bridgeContracts[destinationDomain];
require(destinationContract != address(0x0), "NFTBridge: invalid bridge");
require(destinationContract != address(0x0), "HashiConnextAdapter: invalid bridge");
CallParams memory callParams = CallParams({
to: destinationContract,
callData: callData,
Expand All @@ -66,28 +87,4 @@ contract NFTBridge is Ownable, ERC165, INFTBridge {
});
IConnextHandler(_connext).xcall(xcallArgs);
}

function getBridgeContract(uint32 domain) public view returns (address) {
return _bridgeContracts[domain];
}

function getSelfDomain() public view returns (uint32) {
return _selfDomain;
}

function getConnext() public view returns (address) {
return _connext;
}

function getExecutor() public view returns (address) {
return _executor;
}

function isNFTHashiBridge() public pure returns (bool) {
return true;
}

function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(INFTBridge).interfaceId || super.supportsInterface(interfaceId);
}
}
Loading

1 comment on commit 40585b8

@vercel
Copy link

@vercel vercel bot commented on 40585b8 Jun 17, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.