Skip to content

Commit cf1dc4a

Browse files
committed
add NFT support
1 parent c9212e4 commit cf1dc4a

25 files changed

Lines changed: 1749 additions & 15 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pragma solidity ^0.6.0;
2+
3+
import "./../../../libs/token/ERC721/ERC721.sol";
4+
import "./../../../libs/utils/Address.sol";
5+
6+
contract CrossChainNFTMapping is ERC721 {
7+
using Address for address;
8+
9+
address private lpAddr;
10+
11+
constructor (address _lpAddr, string memory name, string memory symbol) public ERC721(name, symbol) {
12+
require(_lpAddr.isContract(), "lockproxy address must be contract.");
13+
lpAddr = _lpAddr;
14+
}
15+
16+
modifier onlyProxy() {
17+
require(msg.sender == lpAddr, "");
18+
_;
19+
}
20+
21+
function mintWithURI(address to, uint256 tokenId, string memory uri) public onlyProxy {
22+
require(!_exists(tokenId), "token id already exist");
23+
_safeMint(to, tokenId);
24+
_setTokenURI(tokenId, uri);
25+
}
26+
}

contracts/core/cross_chain_manager/interface/IEthCrossChainManager.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.0;
1+
pragma solidity >=0.5.0;
22

33
/**
44
* @dev Interface of the EthCrossChainManager contract for business contract like LockProxy to request cross chain transaction

contracts/core/cross_chain_manager/interface/IEthCrossChainManagerProxy.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.0;
1+
pragma solidity >=0.5.0;
22

33
/**
44
* @dev Interface of the EthCrossChainManagerProxy for business contract like LockProxy to obtain the reliable EthCrossChainManager contract hash.

contracts/core/lock_proxy/LockProxy.sol

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,18 @@ contract LockProxy is Ownable {
154154
return true;
155155
}
156156

157-
158157
function _transferERC20ToContract(address fromAssetHash, address fromAddress, address toAddress, uint256 amount) internal returns (bool) {
159-
IERC20 erc20Token = IERC20(fromAssetHash);
158+
IERC20 erc20Token = IERC20(fromAssetHash);
160159
// require(erc20Token.transferFrom(fromAddress, toAddress, amount), "trasnfer ERC20 Token failed!");
161-
erc20Token.safeTransferFrom(fromAddress, toAddress, amount);
162-
return true;
160+
erc20Token.safeTransferFrom(fromAddress, toAddress, amount);
161+
return true;
163162
}
163+
164164
function _transferERC20FromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
165-
IERC20 erc20Token = IERC20(toAssetHash);
165+
IERC20 erc20Token = IERC20(toAssetHash);
166166
// require(erc20Token.transfer(toAddress, amount), "trasnfer ERC20 Token failed!");
167-
erc20Token.safeTransfer(toAddress, amount);
168-
return true;
167+
erc20Token.safeTransfer(toAddress, amount);
168+
return true;
169169
}
170170

171171
function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) {
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
pragma solidity ^0.6.0;
2+
3+
import "./../../libs/ownership/Ownable.sol";
4+
import "./../../libs/common/ZeroCopySink.sol";
5+
import "./../../libs/common/ZeroCopySource.sol";
6+
import "./../../libs/utils/Utils.sol";
7+
import "./../../libs/utils/Address.sol";
8+
import "./../../libs/token/ERC721/IERC721Metadata.sol";
9+
import "./../../libs/token/ERC721/IERC721Receiver.sol";
10+
import "./../../libs/math/SafeMath.sol";
11+
import "./../cross_chain_manager/interface/IEthCrossChainManager.sol";
12+
import "./../cross_chain_manager/interface/IEthCrossChainManagerProxy.sol";
13+
14+
15+
contract NFTLockProxy is IERC721Receiver, Ownable {
16+
using SafeMath for uint;
17+
using Address for address;
18+
19+
struct TxArgs {
20+
bytes toAssetHash;
21+
bytes toAddress;
22+
uint256 tokenId;
23+
bytes tokenURI;
24+
}
25+
26+
address public managerProxyContract;
27+
mapping(uint64 => bytes) public proxyHashMap;
28+
mapping(address => mapping(uint64 => bytes)) public assetHashMap;
29+
mapping(address => bool) safeTransfer;
30+
31+
event SetManagerProxyEvent(address manager);
32+
event BindProxyEvent(uint64 toChainId, bytes targetProxyHash);
33+
event BindAssetEvent(address fromAssetHash, uint64 toChainId, bytes targetProxyHash);
34+
event UnlockEvent(address toAssetHash, address toAddress, uint256 tokenId);
35+
event LockEvent(address fromAssetHash, address fromAddress, bytes toAssetHash, bytes toAddress, uint64 toChainId, uint256 tokenId);
36+
37+
modifier onlyManagerContract() {
38+
IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract);
39+
require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract");
40+
_;
41+
}
42+
43+
function setManagerProxy(address ethCCMProxyAddr) onlyOwner public {
44+
managerProxyContract = ethCCMProxyAddr;
45+
emit SetManagerProxyEvent(managerProxyContract);
46+
}
47+
48+
function bindProxyHash(uint64 toChainId, bytes memory targetProxyHash) onlyOwner public returns (bool) {
49+
proxyHashMap[toChainId] = targetProxyHash;
50+
emit BindProxyEvent(toChainId, targetProxyHash);
51+
return true;
52+
}
53+
54+
function bindAssetHash(address fromAssetHash, uint64 toChainId, bytes memory toAssetHash) onlyOwner public returns (bool) {
55+
assetHashMap[fromAssetHash][toChainId] = toAssetHash;
56+
emit BindAssetEvent(fromAssetHash, toChainId, toAssetHash);
57+
return true;
58+
}
59+
60+
// /* @notice This function is meant to be invoked by the ETH crosschain management contract,
61+
// * then mint a certin amount of tokens to the designated address since a certain amount
62+
// * was burnt from the source chain invoker.
63+
// * @param argsBs The argument bytes recevied by the ethereum lock proxy contract, need to be deserialized.
64+
// * based on the way of serialization in the source chain proxy contract.
65+
// * @param fromContractAddr The source chain contract address
66+
// * @param fromChainId The source chain id
67+
// */
68+
function unlock(bytes memory argsBs, bytes memory fromContractAddr, uint64 fromChainId) public onlyManagerContract returns (bool) {
69+
TxArgs memory args = _deserializeTxArgs(argsBs);
70+
71+
require(fromContractAddr.length != 0, "from proxy contract address cannot be empty");
72+
require(Utils.equalStorage(proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error!");
73+
74+
require(args.toAssetHash.length != 0, "toAssetHash cannot be empty");
75+
address toAssetHash = Utils.bytesToAddress(args.toAssetHash);
76+
77+
require(args.toAddress.length != 0, "toAddress cannot be empty");
78+
address toAddress = Utils.bytesToAddress(args.toAddress);
79+
80+
bool success;
81+
bytes memory res;
82+
address owner;
83+
bytes memory raw = abi.encodeWithSignature("ownerOf(uint256)", args.tokenId);
84+
(success, res) = toAssetHash.call(raw);
85+
if (success) {
86+
owner = abi.decode(res, (address));
87+
require(owner == address(this) || owner == address(0), "your token ID is not hold by lockproxy.");
88+
if (owner == address(this)) {
89+
raw = abi.encodeWithSignature("safeTransferFrom(address,address,uint256)", address(this), toAddress, args.tokenId);
90+
(success, ) = toAssetHash.call(raw);
91+
require(success, "failed to call safeTransferFrom");
92+
}
93+
}
94+
if (!success || owner == address(0)) {
95+
raw = abi.encodeWithSignature("mintWithURI(address,uint256,string)", toAddress, args.tokenId, string(args.tokenURI));
96+
(success, ) = toAssetHash.call(raw);
97+
require(success, "failed to call mintWithURI to mint a new mapping NFT");
98+
}
99+
100+
emit UnlockEvent(toAssetHash, toAddress, args.tokenId);
101+
return true;
102+
}
103+
104+
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public override returns (bytes4) {
105+
address fromAssetHash = _msgSender();
106+
require(data.length > 0, "length of toAddress can't be zero. ");
107+
require(fromAssetHash.isContract(), "caller must be a contract. ");
108+
109+
bytes memory toAddress;
110+
uint64 toChainId;
111+
bytes memory toAssetHash;
112+
{
113+
(toAddress, toChainId) = _deserializeCallData(data);
114+
toAssetHash = assetHashMap[fromAssetHash][toChainId];
115+
require(toAssetHash.length != 0, "empty illegal toAssetHash");
116+
117+
IERC721Metadata nft = IERC721Metadata(fromAssetHash);
118+
require(nft.ownerOf(tokenId) == address(this), "wrong owner for this token ID");
119+
120+
string memory uri = nft.tokenURI(tokenId);
121+
TxArgs memory txArgs = TxArgs({
122+
toAssetHash: toAssetHash,
123+
toAddress: toAddress,
124+
tokenId: tokenId,
125+
tokenURI: bytes(uri)
126+
});
127+
bytes memory txData = _serializeTxArgs(txArgs);
128+
IEthCrossChainManager eccm = IEthCrossChainManager(IEthCrossChainManagerProxy(managerProxyContract).getEthCrossChainManager());
129+
130+
bytes memory toProxyHash = proxyHashMap[toChainId];
131+
require(toProxyHash.length != 0, "empty illegal toProxyHash");
132+
require(eccm.crossChain(toChainId, toProxyHash, "unlock", txData), "EthCrossChainManager crossChain executed error!");
133+
}
134+
{
135+
emit LockEvent(fromAssetHash, from, toAssetHash, toAddress, toChainId, tokenId);
136+
}
137+
138+
return this.onERC721Received.selector;
139+
}
140+
141+
function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) {
142+
bytes memory buff;
143+
buff = abi.encodePacked(
144+
ZeroCopySink.WriteVarBytes(args.toAssetHash),
145+
ZeroCopySink.WriteVarBytes(args.toAddress),
146+
ZeroCopySink.WriteUint256(args.tokenId),
147+
ZeroCopySink.WriteVarBytes(args.tokenURI)
148+
);
149+
return buff;
150+
}
151+
152+
function _deserializeTxArgs(bytes memory valueBs) internal pure returns (TxArgs memory) {
153+
TxArgs memory args;
154+
uint256 off = 0;
155+
(args.toAssetHash, off) = ZeroCopySource.NextVarBytes(valueBs, off);
156+
(args.toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off);
157+
(args.tokenId, off) = ZeroCopySource.NextUint256(valueBs, off);
158+
(args.tokenURI, off) = ZeroCopySource.NextVarBytes(valueBs, off);
159+
return args;
160+
}
161+
162+
function _deserializeCallData(bytes memory valueBs) internal pure returns (bytes memory, uint64) {
163+
bytes memory toAddress;
164+
uint64 chainId;
165+
uint256 off = 0;
166+
(toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off);
167+
(chainId, off) = ZeroCopySource.NextUint64(valueBs, off);
168+
return (toAddress, chainId);
169+
}
170+
}

contracts/libs/GSN/Context.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.0;
1+
pragma solidity >=0.5.0;
22

33
/*
44
* @dev Provides information about the current execution context, including the

contracts/libs/common/ZeroCopySink.sol

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.0;
1+
pragma solidity >=0.5.0;
22

33
/**
44
* @dev Wrappers over encoding and serialization operation into bytes from bassic types in Solidity for PolyNetwork cross chain utility.
@@ -162,6 +162,32 @@ library ZeroCopySink {
162162
return buff;
163163
}
164164

165+
/* @notice Convert limited uint256 value into bytes
166+
* @param v The uint256 value
167+
* @return Converted bytes array
168+
*/
169+
function WriteUint256(uint256 v) internal pure returns (bytes memory) {
170+
require(v <= uint256(-1), "Value exceeds uint256 range");
171+
bytes memory buff;
172+
173+
assembly{
174+
buff := mload(0x40)
175+
let byteLen := 0x20
176+
mstore(buff, byteLen)
177+
for {
178+
let mindex := 0x00
179+
let vindex := 0x1f
180+
} lt(mindex, byteLen) {
181+
mindex := add(mindex, 0x01)
182+
vindex := sub(vindex, 0x01)
183+
}{
184+
mstore8(add(add(buff, 0x20), mindex), byte(vindex, v))
185+
}
186+
mstore(0x40, add(buff, 0x40))
187+
}
188+
return buff;
189+
}
190+
165191
/* @notice Encode bytes format data into bytes
166192
* @param data The bytes array data
167193
* @return Encoded bytes array

contracts/libs/common/ZeroCopySource.sol

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity ^0.5.0;
1+
pragma solidity >=0.5.0;
22

33
/**
44
* @dev Wrappers over decoding and deserialization operation from bytes into bassic types in Solidity for PolyNetwork cross chain utility.
@@ -173,6 +173,30 @@ library ZeroCopySource {
173173
require(v <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range");
174174
return (v, offset + 32);
175175
}
176+
177+
function NextUint256(bytes memory buff, uint256 offset) internal pure returns (uint256, uint256) {
178+
require(offset + 32 <= buff.length && offset < offset + 32, "NextUint256, offset exceeds maximum");
179+
uint256 v;
180+
assembly {
181+
let tmpbytes := mload(0x40)
182+
let byteLen := 0x20
183+
for {
184+
let tindex := 0x00
185+
let bindex := sub(byteLen, 0x01)
186+
let bvalue := mload(add(add(buff, 0x20), offset))
187+
} lt(tindex, byteLen) {
188+
tindex := add(tindex, 0x01)
189+
bindex := sub(bindex, 0x01)
190+
}{
191+
mstore8(add(tmpbytes, tindex), byte(bindex, bvalue))
192+
}
193+
mstore(0x40, add(tmpbytes, byteLen))
194+
v := mload(tmpbytes)
195+
}
196+
require(v <= uint256(-1), "Value exceeds the range");
197+
return (v, offset + 32);
198+
}
199+
176200
/* @notice Read next variable bytes starting from offset,
177201
the decoding rule coming from multi-chain
178202
* @param buff Source bytes array
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.6.0;
4+
5+
import "./IERC165.sol";
6+
7+
/**
8+
* @dev Implementation of the {IERC165} interface.
9+
*
10+
* Contracts may inherit from this and call {_registerInterface} to declare
11+
* their support of an interface.
12+
*/
13+
contract ERC165 is IERC165 {
14+
/*
15+
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
16+
*/
17+
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
18+
19+
/**
20+
* @dev Mapping of interface ids to whether or not it's supported.
21+
*/
22+
mapping(bytes4 => bool) private _supportedInterfaces;
23+
24+
constructor () internal {
25+
// Derived contracts need only register support for their own interfaces,
26+
// we register support for ERC165 itself here
27+
_registerInterface(_INTERFACE_ID_ERC165);
28+
}
29+
30+
/**
31+
* @dev See {IERC165-supportsInterface}.
32+
*
33+
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
34+
*/
35+
function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
36+
return _supportedInterfaces[interfaceId];
37+
}
38+
39+
/**
40+
* @dev Registers the contract as an implementer of the interface defined by
41+
* `interfaceId`. Support of the actual ERC165 interface is automatic and
42+
* registering its interface id is not required.
43+
*
44+
* See {IERC165-supportsInterface}.
45+
*
46+
* Requirements:
47+
*
48+
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
49+
*/
50+
function _registerInterface(bytes4 interfaceId) internal virtual {
51+
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
52+
_supportedInterfaces[interfaceId] = true;
53+
}
54+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.6.0;
4+
5+
/**
6+
* @dev Interface of the ERC165 standard, as defined in the
7+
* https://eips.ethereum.org/EIPS/eip-165[EIP].
8+
*
9+
* Implementers can declare support of contract interfaces, which can then be
10+
* queried by others ({ERC165Checker}).
11+
*
12+
* For an implementation, see {ERC165}.
13+
*/
14+
interface IERC165 {
15+
/**
16+
* @dev Returns true if this contract implements the interface defined by
17+
* `interfaceId`. See the corresponding
18+
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
19+
* to learn more about how these ids are created.
20+
*
21+
* This function call must use less than 30 000 gas.
22+
*/
23+
function supportsInterface(bytes4 interfaceId) external view returns (bool);
24+
}

0 commit comments

Comments
 (0)