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

init poly-assets #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions contracts/core/assets/erc20_asset/erc20_templete.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
pragma solidity ^0.5.0;


import "./../../../libs/GSN/Context.sol";
import "./../../../libs/token/ERC20/ERC20.sol";
import "./../../../libs/token/ERC20/ERC20Detailed.sol";
import "./../../../libs/ownership/Ownable.sol";

contract ERC20Template is Context, ERC20, ERC20Detailed, Ownable {
mapping(address => bool) private _blacklist;

event BlacklistAdded(address indexed account);
event BlacklistRemoved(address indexed account);

constructor () public ERC20Detailed("Poly-peg-test", "POT", 18) {
_mint(_msgSender(), 1000000000000000000000);
}

function addToBlacklist(address account) external onlyOwner {
require(account != address(0), "Invalid account address");
require(!isBlacklisted(account), "Account is already blacklisted");

_blacklist[account] = true;
emit BlacklistAdded(account);
}

function removeFromBlacklist(address account) external onlyOwner {
require(account != address(0), "Invalid account address");
require(isBlacklisted(account), "Account is not blacklisted");

_blacklist[account] = false;
emit BlacklistRemoved(account);
}

function transfer(address recipient, uint256 amount) public returns (bool) {
require(!isBlacklisted(msg.sender), "Your account is already blacklisted");
require(!isBlacklisted(recipient), "Recipient's account is already blacklisted");

return super.transfer(recipient, amount);
}

function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
require(!isBlacklisted(sender), "Sender's account is already blacklisted");
require(!isBlacklisted(recipient), "Recipient's account is already blacklisted");

return super.transferFrom(sender, recipient, amount);
}

function isBlacklisted(address account) public view returns (bool) {
return _blacklist[account];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pragma solidity ^0.5.0;

/**
* @dev Interface of the EthCrossChainManager contract for business contract like LockProxy to request cross chain transaction
*/
interface IEthCrossChainManager {
function crossChain(uint64 _toChainId, bytes calldata _toContract, bytes calldata _method, bytes calldata _txData) external returns (bool);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pragma solidity ^0.5.0;

/**
* @dev Interface of the EthCrossChainManagerProxy for business contract like LockProxy to obtain the reliable EthCrossChainManager contract hash.
*/
interface IEthCrossChainManagerProxy {
function getEthCrossChainManager() external view returns (address);
}
189 changes: 189 additions & 0 deletions contracts/core/lock_proxy/LockProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
pragma solidity ^0.5.0;

import "./../../libs/ownership/Ownable.sol";
import "./../../libs/common/ZeroCopySource.sol";
import "./../../libs/common/ZeroCopySink.sol";
import "./../../libs/utils/Utils.sol";
import "./../../libs/token/ERC20/SafeERC20.sol";
import "./../cross_chain_manager/interface/IEthCrossChainManager.sol";
import "./../cross_chain_manager/interface/IEthCrossChainManagerProxy.sol";


contract LockProxy is Ownable {
using SafeMath for uint;
using SafeERC20 for IERC20;

struct TxArgs {
bytes toAssetHash;
bytes toAddress;
uint256 amount;
}
address public managerProxyContract;
mapping(uint64 => bytes) public proxyHashMap;
mapping(address => mapping(uint64 => bytes)) public assetHashMap;
mapping(address => bool) safeTransfer;

event SetManagerProxyEvent(address manager);
event BindProxyEvent(uint64 toChainId, bytes targetProxyHash);
event BindAssetEvent(address fromAssetHash, uint64 toChainId, bytes targetProxyHash, uint initialAmount);
event UnlockEvent(address toAssetHash, address toAddress, uint256 amount);
event LockEvent(address fromAssetHash, address fromAddress, uint64 toChainId, bytes toAssetHash, bytes toAddress, uint256 amount);

modifier onlyManagerContract() {
IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract);
require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract");
_;
}

function setManagerProxy(address ethCCMProxyAddr) onlyOwner public {
managerProxyContract = ethCCMProxyAddr;
emit SetManagerProxyEvent(managerProxyContract);
}

function bindProxyHash(uint64 toChainId, bytes memory targetProxyHash) onlyOwner public returns (bool) {
proxyHashMap[toChainId] = targetProxyHash;
emit BindProxyEvent(toChainId, targetProxyHash);
return true;
}

function bindAssetHash(address fromAssetHash, uint64 toChainId, bytes memory toAssetHash) onlyOwner public returns (bool) {
assetHashMap[fromAssetHash][toChainId] = toAssetHash;
emit BindAssetEvent(fromAssetHash, toChainId, toAssetHash, getBalanceFor(fromAssetHash));
return true;
}

/* @notice This function is meant to be invoked by the user,
* a certin amount teokens will be locked in the proxy contract the invoker/msg.sender immediately.
* Then the same amount of tokens will be unloked from target chain proxy contract at the target chain with chainId later.
* @param fromAssetHash The asset address in current chain, uniformly named as `fromAssetHash`
* @param toChainId The target chain id
*
* @param toAddress The address in bytes format to receive same amount of tokens in target chain
* @param amount The amount of tokens to be crossed from ethereum to the chain with chainId
*/
function lock(address fromAssetHash, uint64 toChainId, bytes memory toAddress, uint256 amount) public payable returns (bool) {
require(amount != 0, "amount cannot be zero!");


require(_transferToContract(fromAssetHash, amount), "transfer asset from fromAddress to lock_proxy contract failed!");

bytes memory toAssetHash = assetHashMap[fromAssetHash][toChainId];
require(toAssetHash.length != 0, "empty illegal toAssetHash");

TxArgs memory txArgs = TxArgs({
toAssetHash: toAssetHash,
toAddress: toAddress,
amount: amount
});
bytes memory txData = _serializeTxArgs(txArgs);

IEthCrossChainManagerProxy eccmp = IEthCrossChainManagerProxy(managerProxyContract);
address eccmAddr = eccmp.getEthCrossChainManager();
IEthCrossChainManager eccm = IEthCrossChainManager(eccmAddr);

bytes memory toProxyHash = proxyHashMap[toChainId];
require(toProxyHash.length != 0, "empty illegal toProxyHash");
require(eccm.crossChain(toChainId, toProxyHash, "unlock", txData), "EthCrossChainManager crossChain executed error!");

emit LockEvent(fromAssetHash, _msgSender(), toChainId, toAssetHash, toAddress, amount);

return true;

}

// /* @notice This function is meant to be invoked by the ETH crosschain management contract,
// * then mint a certin amount of tokens to the designated address since a certain amount
// * was burnt from the source chain invoker.
// * @param argsBs The argument bytes recevied by the ethereum lock proxy contract, need to be deserialized.
// * based on the way of serialization in the source chain proxy contract.
// * @param fromContractAddr The source chain contract address
// * @param fromChainId The source chain id
// */
function unlock(bytes memory argsBs, bytes memory fromContractAddr, uint64 fromChainId) onlyManagerContract public returns (bool) {
TxArgs memory args = _deserializeTxArgs(argsBs);

require(fromContractAddr.length != 0, "from proxy contract address cannot be empty");
require(Utils.equalStorage(proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error!");

require(args.toAssetHash.length != 0, "toAssetHash cannot be empty");
address toAssetHash = Utils.bytesToAddress(args.toAssetHash);

require(args.toAddress.length != 0, "toAddress cannot be empty");
address toAddress = Utils.bytesToAddress(args.toAddress);


require(_transferFromContract(toAssetHash, toAddress, args.amount), "transfer asset from lock_proxy contract to toAddress failed!");

emit UnlockEvent(toAssetHash, toAddress, args.amount);
return true;
}

function getBalanceFor(address fromAssetHash) public view returns (uint256) {
if (fromAssetHash == address(0)) {
// return address(this).balance; // this expression would result in error: Failed to decode output: Error: insufficient data for uint256 type
address selfAddr = address(this);
return selfAddr.balance;
} else {
IERC20 erc20Token = IERC20(fromAssetHash);
return erc20Token.balanceOf(address(this));
}
}
function _transferToContract(address fromAssetHash, uint256 amount) internal returns (bool) {
if (fromAssetHash == address(0)) {
// fromAssetHash === address(0) denotes user choose to lock ether
// passively check if the received msg.value equals amount
require(msg.value != 0, "transferred ether cannot be zero!");
require(msg.value == amount, "transferred ether is not equal to amount!");
} else {
// make sure lockproxy contract will decline any received ether
require(msg.value == 0, "there should be no ether transfer!");
// actively transfer amount of asset from msg.sender to lock_proxy contract
require(_transferERC20ToContract(fromAssetHash, _msgSender(), address(this), amount), "transfer erc20 asset to lock_proxy contract failed!");
}
return true;
}
function _transferFromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
if (toAssetHash == address(0x0000000000000000000000000000000000000000)) {
// toAssetHash === address(0) denotes contract needs to unlock ether to toAddress
// convert toAddress from 'address' type to 'address payable' type, then actively transfer ether
address(uint160(toAddress)).transfer(amount);
} else {
// actively transfer amount of asset from lock_proxy contract to toAddress
require(_transferERC20FromContract(toAssetHash, toAddress, amount), "transfer erc20 asset from lock_proxy contract to toAddress failed!");
}
return true;
}


function _transferERC20ToContract(address fromAssetHash, address fromAddress, address toAddress, uint256 amount) internal returns (bool) {
IERC20 erc20Token = IERC20(fromAssetHash);
// require(erc20Token.transferFrom(fromAddress, toAddress, amount), "trasnfer ERC20 Token failed!");
erc20Token.safeTransferFrom(fromAddress, toAddress, amount);
return true;
}
function _transferERC20FromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
IERC20 erc20Token = IERC20(toAssetHash);
// require(erc20Token.transfer(toAddress, amount), "trasnfer ERC20 Token failed!");
erc20Token.safeTransfer(toAddress, amount);
return true;
}

function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) {
bytes memory buff;
buff = abi.encodePacked(
ZeroCopySink.WriteVarBytes(args.toAssetHash),
ZeroCopySink.WriteVarBytes(args.toAddress),
ZeroCopySink.WriteUint255(args.amount)
);
return buff;
}

function _deserializeTxArgs(bytes memory valueBs) internal pure returns (TxArgs memory) {
TxArgs memory args;
uint256 off = 0;
(args.toAssetHash, off) = ZeroCopySource.NextVarBytes(valueBs, off);
(args.toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off);
(args.amount, off) = ZeroCopySource.NextUint255(valueBs, off);
return args;
}
}
28 changes: 28 additions & 0 deletions contracts/libs/GSN/Context.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
pragma solidity ^0.5.0;

/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
* Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/GSN/Context.sol
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
// solhint-disable-previous-line no-empty-blocks

function _msgSender() internal view returns (address payable) {
return msg.sender;
}

function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
Loading